最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
如何让Oracle PL/SQL任务失败后自动重试
时间:2026-08-24 09:20:50 编辑:袖梨 来源:一聚教程网
PL/SQL 无法捕获连接中断错误,因为 ORA-03114、ORA-03113 发生在 SQL*Net 网络层,语句未送达数据库或响应丢失,PL/SQL 引擎已失上下文,不进入 EXCEPTION 分支;重试必须由客户端或应用层实现。
PL/SQL 本身无法实现“自动重试”——连接中断、会话失效类错误根本进不了 EXCEPTION 块,重试逻辑必须由客户端或应用层控制。
为什么 PL/SQL 的 WHEN OTHERS 捕获不到连接失败?
ORA-03114(not connected to ORACLE)、ORA-03113(communication channel failure)等错误发生在 SQL*Net 网络层,语句甚至没发到数据库,或响应丢失,PL/SQL 引擎已失去执行上下文,直接报错退出。此时 EXCEPTION 分支完全不触发,WHEN OTHERS 形同虚设。
常见误判场景:
- 在存储过程中写
WHEN OTHERS THEN RAISE_APPLICATION_ERROR(-20001, 'retry later');,但连接断了根本不会执行这行 - 用
DBMS_SCHEDULER调用一个带事务的存储过程,中间网络抖动导致作业状态变成STOPPED,但日志里看不到任何异常处理痕迹 - 以为
SQLCODE能拿到 -3113 就能做判断,实际SQLCODE在连接中断时根本不可用(变量未初始化或返回 0)
真正可行的重试策略在哪做?
重试动作必须落在调用 PL/SQL 的外部环节,且需区分场景:
-
SQL*Plus 批处理脚本:用 shell 或 bat 控制循环 + 退出码判断。例如 Linux 下:
until sqlplus user/pass@db @job.sql; do sleep 10; done,配合EXIT WHEN SQLCODE != 0;在脚本末尾显式退出 -
Java 应用:捕获
SQLException,检查getErrorCode()是否为 3113/3114,手动重建Connection并重试逻辑(注意:Oracle JDBC 驱动默认不支持 auto-reconnect,autoReconnect=true参数无效) -
DBMS_SCHEDULER 作业:不能靠 PL/SQL 自身重试,但可配置
MAX_FAILURES和RESTARTABLE属性,再结合DBMS_SCHEDULER.ADD_EVENT_RULE监听job_failed事件,触发另一个作业做补偿 - PL/SQL Developer / SQL Developer:无内置重试机制,只能靠“执行前验证连接”(Tools → Preferences → Connection → Check connection),失败时弹窗提示,由人工点重试
DBMS_SCHEDULER 中怎么模拟“失败后重试”?
虽然不能让单个作业内部循环重试,但可以组合调度能力逼近效果:
- 创建主作业调用你的存储过程,设置
max_failures => 3、restartable => TRUE - 定义一个事件规则:
DBMS_SCHEDULER.ADD_EVENT_RULE监听'oracle.scheduler.job_failed',条件是event_type = 'JOB_FAILED'且job_name = 'MY_MAIN_JOB' - 该规则触发一个“重试作业”,它先等待几秒(
DBMS_LOCK.SLEEP(5)),再调用同一存储过程;可限制最多触发 2 次,避免无限循环 - 注意:所有重试作业都得单独提交事务,不能依赖原作业的事务上下文
示例关键参数:job_action => 'BEGIN my_proc; END;',不是 my_proc(后者不支持参数,且无法捕获执行结果)
最容易被忽略的细节
很多人花时间在 PL/SQL 里加 IF SQLCODE IN (-3113, -3114) THEN ...,却忘了——这个判断只在错误“被抛出并进入 EXCEPTION”时才有效;而连接中断时,SQLCODE 根本没机会被赋值,整块代码已被跳过。真正的防线不在存储过程体里,而在连接保活(如 SQL Developer 的 Keep Alive)、客户端超时设置、连接池 validation-query 配置,以及作业失败后的事件驱动补偿链路上。