job(文中用j1代之)执行存储过程(p1)。p1中通过dblink对远程表做查询。另外P1中对others异常做了处理,把所有异常记录到日志中。
由于厂里停电,导致到远程数据库服务器的通信中断。j1在当前也停止了工作。查看j1,broken 为 Y,失败16次。
这就奇怪了,既然做了异常处理,p1中的所有异常(包括网络异常)都会被捕获,并记录到日志,j1根本就不会意识到p1执行失败,那怎么会出现失败16次导致j1 broken的呢?
查询日志发现,在j1上次执行时间内,根本没有该存储过程的任何错误日志。也就是说,j1失败16次但p1可能根本没执行。
查看了一下j1失败那段时间的alter日志有所发现:
Tue Apr 26 17:47:10 2011
Errors in file d:/oracle/product/10.2.0/admin/l25steel/bdump/l25steel_j000_4120.trc:
ORA-12012: 自动执行作业 269 出错
ORA-04045: 在重新编译/重新验证 COMMUNICATION.DATAFROML3 时出错
ORA-04052: 在查找远程对象 SM.PP_SM_IRON_SAMPLE_T@DBLINK_L3IRON_L25 时出错
ORA-00604: 递归 SQL 级别 4 出现错误
ORA-12170: TNS: 连接超时
原来p1是被重新编译了,由于网络中断导致抄不到远程对象,因而编译失败。这要就解释了为什么p1没被执行而j1失败了16次。
又是网络惹的祸。但怎么才能让程序包容网络错误呢?oracle非要重新编译存储过程,也不知道它什么时候才重新编译。
办法就是使用动态sql语句,骗过oracle,不让它帮你编译。
例如原来的存储过程p1,新建一个过程p2:
begin
strsql:='p1';
execute strsql;
exception when others
****
end
这样p1就作为动态语句在p2中执行。即使p1无效了(被删了,或者编译失败),p2仍然有效,能被执行。
不过要提醒一点的是,这样可能把原本能在静态编译时发现的错误引入到运行时。
开发人员和管理员必须好好读日志了。