Informix 高级培训教材(四)
4.2.6. 增加连接成员:
执行 picc21 上 $INformIXDIR/cdr/crtmeb.sh
脚本内容如下: TuhdxFz&
cdr define server -s datadbs -r datadbs -A /tmp/ats -R /tmp/ris
--connect=grp_ibm500 --init --sync=grp_picc21 grp_ibm500
注:确保 ibm500 上的 datadbs 和 datadbs 已建立
确保 ibm500 上的 /tmp/ats 和 /tmp/ris 目录已建立
4.2.7. 确定成员已连接
执行 picc21 上执行 cdr list server
系统显示如下说明新成员已连接:
SERVER ID STATE STATUS CONNECTION CHANGED
grp_ibm500 2 Active Connected Dec 7 14:54:17 2001
grp_picc21 1 Active Local
1. 在增加一个成员时也会在 ibm500 上的 database server 上建立一个名为
syscdr 的 database ,因此除了用 cdr list server 外,也可以用 dbaccess 命
令看是否建立了 syscdr 来确定一个成员是否正确加入。
2. 确保 picc21 和 ibm500 已做时间同步(参见附录)
4.2.8. 定义 replicate
执行 picc21 上 $INformIXDIR/cdr/repdef.sh
脚本内容如下: Zq/
cdr def repl picc2_a_charge_ratio -C ignore -S row -T -R " PO
picc2@grp_picc21:cbps.a_charge_ratio " " select * from a_charge_ratio " " RO
picc2@grp_ibm500:cbps.a_charge_ratio " " select * from a_charge_ratio "
cdr def repl picc2_a_comm_ratio -C ignore -S row -T -R " PO
picc2@grp_picc21:cbps.a_comm_ratio " " select * from a_comm_ratio " " RO
picc2@grp_ibm500:cbps.a_comm_ratio " " select * from a_comm_ratio "
4.2.9. 确定 replicate 建立
执行 picc21 上执行 cdr list repl
系统显示如下说明 replicate 已建立(但为激活):
REPLICATE STATE CONFLICT FREQUENCY OPTIONS
picc2_a_charge_ratio INACTIVE ignore immediate row,ris,triggers
picc2_a_comm_ratio INACTIVE ignore immediate row,ris,triggers
4.2.10. 启动 replicate
执行 picc21 上 $INformIXDIR/cdr/repstart.sh
脚本内容如下:
cdr start repl picc2_a_charge_ratio
cdr start repl picc2_a_comm_ratio
4.2.11. 确定 replicate 已启动
执行 picc21 上执行 cdr list repl
系统显示如下说明 replicate 已激活:
REPLICATE STATE CONFLICT FREQUENCY OPTIONS
picc2_a_charge_ratio ACTIVE ignore immediate row,ris,triggers
picc2_a_comm_ratio ACTIVE ignore immediate row,ris,triggers
4.2.12. 关闭 replicate
执行 picc21 上 $INformIXDIR/cdr/repstop.sh
脚本内容如下:
cdr stop repl picc2_a_charge_ratio
cdr stop repl picc2_a_comm_ratio
4.2.13. 维护 CDR ?/@#{J
CDR 配置完成后,系统将按照配置自动完成数据复制功能。根据需要也可以停止 CDR 和
重新启动 CDR, 或者修改 CDR 的配置。下面列出几种需要对 CDR 进行维护的情况:
1 . 使用 dbexport 卸载数据库时,需要通过如下命令停止 CDR
cdr stop
2 . 可以通过如下命令重新启动 CDR
cdr start
3 . 修改复制时间间隔时,可以通过如下命令修改 replicate 的配置
cdr modify repl -a 24:00 repl_name
注:将复制间隔修改为每天 24:00 开始复制
可以使用脚本 $INformIXDIR/cdr/repmdf.sh 进行成批修改
4 . 需要停止某个 replicate 时,可以通过如下命令完成
cdr stop repl repl_name
注:可以使用脚本 $INformIXDIR/cdr/repstop.sh 停止一批 replicate
5 . 需要删除某个 replicate 时,可以通过如下命令完成
cdr delete repl repl_name
注:可以使用脚本 $INformIXDIR/cdr/repdel.sh 进行成批修改
6 . 需要删除 replicate server 时,可以通过如下命令完成
cdr delete server server_name
4.2.14. 监控 CDR 工作情况
1. 可以通过 2.3 、 2.5 、 2.7 章节中的命令分别监控 replicate server 、
participant 和 replicate 的状态是否正常。
2. 通过查看 /tmp/ats 和 /tmp/ris 目录,如有文件存在说明有复制不成功的记录,
通过 vi 查看复制不成功的具体原因。
3. 通过查看 $INformIXDIR/online.log 文件,判断 CDR 运行情况。
4.2.15. CDR 出错处理
一旦有某个表的数据复制不成功, 完成如下步骤恢复该表的数据一致性:
1 . Suspend 该表的复制
2 . 删除该表在复制库中的数据
3 . 从生产库 unload 该表数据,再在复制库 load 该表数据
4 . resum 该表的复制
4.3. CDR 的时间同步
有关 ntp(Network time protocol) 的配置,必须在 CDR 定义之前进行。两台主机必须
通过 ntp 做时钟同步,哪台主机做 ntp Server 是无所谓的。在跨平台的情况配置有所不同
这里介绍了两种情况: 2 台 SCO 主机和 1 台 AIX 、 1 台 SCO
² 2 台主机都是 SCO 平台
ntp Server 端:
1 ) 编辑 /etc/ntp.conf( 有可能无此文件,可以新建一个文件,其 owner 和 group 都是 bin)
内容如下:
server 127.127.1.0
2 ) 修改 /etc/tcp 文件
在 /etc/tcp 文件中查 xntpd 行,删除 tickadj 一行,下一行的 xntpd 后加 -g ,
ntp Client 端:
1 ) 编辑 /etc/ntp.conf( 有可能无此文件,可以新建一个文件,其 owner 和 group 都是 bin)
内容如下:
peer ( ntp Server 的 IP 地址)如: peer 11.137.75.131
2 ) 修改 /etc/tcp 文件
在 /etc/tcp 文件中查 xntpd 行,保留 tickadj 一行,下一行的 xntpd 后加 -g
3 )跟踪两台主机 ntp 是否同步成功:查 /usr/adm 下的 syslog
tail -f syslog
ntp Server 端应有 ntp Server 启动的信息
ntp Client 端应每隔 5-10 分钟有与 ntp Server 握手的信息
² 1 台主机是 SCO 平台, 1 台主机是 AIX 平台
1)server 端必须定义在 AIX 平台上且 /etc/ntp.conf 文件如下
server 127.127.1.0
fudge 127.127.1.0 refid
注: s 7a 是 time server 所在的主机名
2)xntpd -g ( 或 /etc/rc.tcpid 中加入该行,重启机 )
3 )查看两台主机是否同步:运行 ntpqà readlist
1 . 存储过程的使用
公共模块,用存储过程代替函数,可使代码统一。
Client /Server 模式,用存储过程代替函数,减少 client 端应用,减少传输量,提高效率。
在 SQL 语句中,需要使用存储过程处理复杂的事情。这一点在描述中使用的特别普遍。
2 . 存储过程的调用
存储过程有两种调用方式
(1). select getday( “ 1998/1/1 ”,1,”month”) from exec
(2). Execute procedure getday(“ 1998/1/1 ”,1,”month”)
注: exec 为一只有一条记录的表
当存储过程以 with resume 返回,返回值超过一列时,只能用第二种调用方式。
这两种方式有一点区别,第二种方式执行的一定是存储过程,第一种方式则不一定。
如下所示:
select mod(7,3) from exec 执行的是系统过程
execute procedure mod(7,4) 执行的是自己定义的存储过程 mod 。
3 . 存储过程的调试
存储过程的调试通常采用两种方法。
( 1 ) 设置 debug file , 以 trace 方式。
( 2 ) 用 return .. with resume 方式调试。
例: 1
create procedure getday(t_day date,t_int int ,t_flag char(10)) returning date;
define i int;
define tt_day date;
DEFINE ESQL,EISAM INT;
DEFINE ETEXT CHAR(80);
Set debug file to “sun.tmp”;
let tt_day= " 18991231 " ;
let i=0;
if t_flag= " year " then
while 1=1
on exception in(-1267) A|)
let i=i+1;
end exception
let tt_day=t_day-i;
let tt_day=tt_day+t_int units year;
trace tt_day;
exit while;
end while;
elif t_flag= " month " then
while 1=1
on exception in(-1267)
let i=i+1;
end exception
let tt_day=t_day-i;
let tt_day=tt_day+t_int units month;
trace tt_day;
exit while;
end while;
elif t_flag= " day " then
let tt_day=t_day+t_int units day;
else
let tt_day=t_day;
end if;
return tt_day;
end procedure;
例: 2
create procedure getday(t_day date,t_int int ,t_flag char(10)) returning date;
define i int;
define tt_day date;
DEFINE ESQL,EISAM INT;
DEFINE ETEXT CHAR(80);
let tt_day= " 18991231 " ;
let i=0;
if t_flag= " year " then
while 1=1
on exception in(-1267)
let i=i+1;
end exception
let tt_day=t_day-i;
let tt_day=tt_day+t_int units year;
return tt_day with resume;
exit while;
end while;
elif t_flag= " month " then
while 1=1
on exception in(-1267)
let i=i+1;
end exception
let tt_day=t_day-i;
let tt_day=tt_day+t_int units month;
return tt_day with resume;
exit while;
end while;
elif t_flag= " day " then
let tt_day=t_day+t_int units day;
else
let tt_day=t_day;
end if;
return tt_day;
end procedure;
4 . 存储过程中错误的捕获
在 4gl 程序中,有 whenever error continue 语句,屏蔽错误,在存储过程中不能使用
whenever error continue ,但可以使用 on exception 来捕获错误,使存储过程继续执行。
例 1
create procedure get_pick_list(p_order_num int) returning int;
define x integer; b
on exception in (-206)
call log_error(-206);
end exception with resume;
select
let x=y;
return x;
end procedure;
例 2 : 以下存储过程用以计算时间,标准为向前靠,如 “ 1998/1/31 ”
日加一个月为 “ 1998/2/28 ”
create procedure getday(t_day date,t_int int ,t_flag char(10)) returning date;
define i int;
define tt_day date;
DEFINE ESQL,EISAM INT;
DEFINE ETEXT CHAR(80);
let tt_day= " 18991231 " ;
let i=0;
if t_flag= " year " then
while 1=1
on exception in(-1267)
let i=i+1;
end exception
let tt_day=t_day-i;
let tt_day=tt_day+t_int units year;
exit while;
end while;
elif t_flag= " month " then
while 1=1 No
on exception in(-1267)
let i=i+1;
end exception
let tt_day=t_day-i;
let tt_day=tt_day+t_int units month;
exit while;
end while;
elif t_flag= " day " then
let tt_day=t_day+t_int units day;
else
let tt_day=t_day;
end if;
return tt_day;
end procedure;
注:在存储过程中, on exception 的使用有作用域,若使用在循环体内,其作用域为循环体,
退出即退出本次循环。若使用在 begin work 与 commit work 内 , 其作用域为 begin work 内,退出
即退出该事物体。以下几个实例给予说明:
例 1 :
create procedure get(t_day date,t_int int )
returning date;
define i int;
define tt_day date;
on exception in(-1267) ---- 此语句的作用域为整个存储过程
let i=i+1; ---- 此语句必须为存储过程的第一条语句
end exception with resume; ------ 注意有 with resume
set debug file to " sun.tmp " ;
let tt_day= " 18991231 " ;
let i=0;
while 1=1
trace i;
trace tt_day;
let tt_day=t_day-i;
let tt_day=tt_day+t_int units month; --- 此处出错后执行 while 循
环外的第一条语句,即下面黑色字体的语句。若无 with resume 则退出存储过
程。
trace " ok " ;
trace tt_day;
exit while;
end while;
trace " ok " ; --- 出错后执行此语句
trace tt_day;
return tt_day;
end procedure;
例 2 :
create procedure get(t_day date,t_int int )
returning date;
define i int;
define tt_day date;
set debug file to " sun.tmp " ;
let tt_day= " 18991231 " ;
let i=0;
while 1=1
on exception in(-1267) ---- 此语句的作用域为 while 循环
let i=i+1; ---- 必须为 while 循环内的第一条语句
end exception; ------ 注意无 with resume
trace i;
trace tt_day;
let tt_day=t_day-i;
let tt_day=tt_day+t_int units month; --- 此处出错后执行 while 循
环的下一循环,相当于 continue while 。若有 with resume 则执行下面一条语 )
句,即黑色字体的语句。
trace " ok " ;
trace tt_day;
exit while;
end while;
trace tt_day;
return tt_day;
end procedure;
5 . 存储过程的效率
系统中的存储过程尽量放在一个 extents 中,最好不要超过 8 个 extents. ( 注: extents 连
续的存储空间 ) ,因此可定期将系统中的所有存储过程重建。
存储过程中使用 set optimization low 可提高存储过程的效率,前提是该存储过程中所涉及
的表的结构,字段无任何改动。
更为重要的是存储过程的效率与存储过程中的语句的写法很有关系。有时某一语句换一种写
法,存储过程的效率可得到极大提高。
例如:
create procedure true_rqstart( p_main_cert like rta1.bm_cert,p_kinds like
rta1.kinds ,t_rq_start like rta1.rq_start)
returning date;
define p_rq_start date;
define tt_rq_start date;
define t_bm_min char(20);
define t_bm_max char(20);
define p_bm_cert2 char (20);
let p_rq_start = " 1899/12/31 " ;
le tt_bm_min = p_kinds[1,3] || p_main_cert[4,7] || " 0000000000000 " ;
let t_bm_max = p_kinds[1,3] || p_main_cert[4,7] || " 9999999999999 " ;
-- foreach select bm_cert2 into p_bm_cert2 from rta 1f where
--bm_cert1=p_main_cert and bm_cert2>=t_bm_min and bm_cert2<= t_bm_max
-- select rq_start into tt_rq_start from rta1 where bm_cert = p_bm_cert2;
-- if tt_rq_start > p_rq_start then FST
-- let p_rq_start = tt_rq_start;
-- end if;
--end foreach;
-- 以上改为以下语句,效率显著提高,从 30 分到不到 1 秒
-- 该语句有多种写法,子查询,连表等,但在该存储过程中,以下写法效率最高
foreach select bm_cert2 into p_bm_cert2 from rta 1f where bm_cert1=p_main_cert
if p_bm_cert2[1,3]=p_kinds then
select rq_start into tt_rq_start from rta1 where bm_cert = p_bm_cert2;
if tt_rq_start > p_rq_start then
let p_rq_start = tt_rq_start;
end if;
end if;
end foreach;
if p_rq_start = " 1899/12/31 " then
if (t_rq_start + 35 units day ) >= today then
return null;
else
return today;
end if;
else
if p_rq_start<>(t_rq_start -1 units year) and (t_rq_start-30 units SDP day)< (p_rq_start+1 units year) then
let p_rq_start=p_rq_start+1 units year;
return p_rq_start;
else
return null;
end if
end if }
end procedure;
6 . 存储过程内 SQL 语句执行情况的判断
在 4GL 程序中对 SQL 语句执行情况的判断可用 status, sqlca 等来实现。 但在存储过程中,不能直接使用这些全局变量。但可通过 dbinfo() 来实现。
create procedure num_rows()
returning int;
define num_rows int;
delete from orders where customer_num=104;
let num_rows=dbinfo(“sqlca.sqlerrd[3]”);
return num_rows;