Oracle作业V$SQL_MONITOR显示DONE(ERROR)但未执行完成问题咨询
异常产生的核心原因
- 调度器状态与SQL Monitor上报的异步错位:
DBMS_SCHEDULER.RUN_JOB指定use_current_session => FALSE时,作业会以独立的后台服务进程运行,调度器的作业状态更新、V$SQL_MONITOR的执行状态上报是两条独立的异步链路。如果调度器的主进程先遇到临时内部错误(比如共享内存锁冲突、进程通信超时),会先将作业标记为DONE (ERROR),但此时已启动的工作进程还未收到终止信号,会继续在后台执行,就会出现状态和实际运行情况不符的问题。 - 作业进程的生命周期脱离父块控制:如果作业的PL/SQL脚本中包含自治事务声明、嵌套调用其他独立作业、调用操作系统级外部命令,父PL/SQL块抛出异常被调度器捕获标记为失败后,自治事务、子作业、外部进程的生命周期不受父块终止影响,会继续在后台运行。
- 作业运行状态判断逻辑的误差:使用
DBMS_SCHEDULER.STOP_JOB(force => FALSE)作为作业运行的判断探针本身存在误差,该接口的返回结果依赖调度器元数据的同步状态,元数据存在延迟时就会出现返回结果和实际运行情况不一致的问题。
可行排查思路
- 替换作业运行状态的判断逻辑:放弃使用
STOP_JOB做探针,直接查询调度器的运行时元数据视图判断作业状态,执行语句:
有返回值则说明作业确实处于运行状态,该结果比SELECT 1 FROM DBA_SCHEDULER_RUNNING_JOBS WHERE JOB_NAME = UPPER(:cName) AND OWNER = UPPER(:job_owner);STOP_JOB返回值的准确性高一个数量级。 - 开启作业全量日志:执行命令
DBMS_SCHEDULER.SET_ATTRIBUTE(cName, 'LOGGING_LEVEL', DBMS_SCHEDULER.LOGGING_FULL);,所有作业运行的状态变更、错误信息都会写入DBA_SCHEDULER_JOB_RUN_DETAILS视图,异常发生时对比该视图的报错时间、错误码和V$SQL_MONITOR的记录,确认是否为状态上报错位。 - 排查作业脚本的逻辑:检查作业是否使用
PRAGMA AUTONOMOUS_TRANSACTION声明自治事务、是否嵌套调用DBMS_SCHEDULER.RUN_JOB启动其他作业、是否通过HOST/DBMS_PIPE/外部程序类型的调度器任务调用操作系统命令,这类逻辑都会产生脱离父作业控制的子进程。 - 关联验证SQL Monitor记录:确认你看到的
DONE (ERROR)记录是否匹配本次作业运行,用V$SQL_MONITOR的SID、SERIAL#、EXECUTION_START_TIME和DBA_SCHEDULER_RUNNING_JOBS的SESSION_ID、SESSION_SERIAL#、START_DATE做关联,避免匹配到历史错误记录。
解决方案
- 针对状态异步延迟问题:调用
DBMS_SCHEDULER.RUN_JOB后等待1-2秒再查询状态,或者每次查询状态前执行DBMS_SCHEDULER.REFRESH_SCHEDULER_STATE强制同步调度器最新状态,消除元数据同步延迟的影响。 - 针对子进程脱离控制问题:修改作业脚本,增加父子进程的心跳检测逻辑,父作业退出前必须主动终止所有派生的子进程、自治事务,或给子进程设置独立的运行超时阈值。
- 针对调度器临时错误问题:给作业配置自动重试策略,执行
DBMS_SCHEDULER.SET_ATTRIBUTE(cName, 'MAX_RETRIES', 3);,同时设置RESTART_ON_RECOVERY = TRUE,规避节点抖动、调度器锁冲突等临时错误导致的状态误报。 - 临时规避方案:发现状态异常时先查询
DBA_SCHEDULER_RUNNING_JOBS获取作业对应的会话ID、序列号,执行ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;终止进程,比force => TRUE的STOP_JOB操作更可控,不会误杀其他作业。
内容的提问来源于stack exchange,提问作者Altiy Zemlytskiy
相关产品推荐
相关产品推荐

