接到告警的第一反应,大多数人会直接盯着DBA_SCHEDULER_JOB_RUN_DETAILS看报错,发现没有记录,就一头扎进应用日志里翻。但做过几年数据库运维的人都会明白,Oracle Scheduler任务故障诊断的难点从来不在“查SQL”,而在“从哪查起”和“怎么把会话、任务、日志三者关联起来”。
这篇文章我想把Oracle Scheduler排障这件事,从方法论到实操,从数据字典到状态机,从卡死任务到权限问题,系统性地梳理一遍。内容不会绕弯子,全部来自真实生产环境踩过的坑,适合正在为定时任务焦头烂额的DBA,也适合刚接手调度系统、想建立排查框架的运维同学。
1. 先建立全局观:任务故障诊断的四步框架
1.1 你可能遇到的几种故障类型
Oracle Scheduler(常被称为DBMS_SCHEDULER)在生产环境里承担着数据抽取、报表生成、存储过程批处理、外部脚本调用等关键职责。一旦任务跑挂,直接影响业务数据的时效性。根据我这些年的经历,任务故障基本可以归为四大类:
- 任务根本没启动:调度时间到了,但
DBA_SCHEDULER_JOBS里的状态还是SCHEDULED,日志表里没有新记录。这类问题大多出在调度属性、窗口、时区或资源限制上。 - 任务启动了但一直RUNNING:会话还挂着,任务在运行中,但实际可能已经死锁、在等待外部资源、或者程序逻辑进入死循环。这类问题最隐蔽,也最耗费精力。
- 任务执行报错FAILED:日志表里明确记录了
ORA-错误,这类问题相对好定位,但要分清是任务程序本身的错误,还是Oracle调度器层面的错误。 - 结果错误但状态是SUCCEEDED:状态显示成功,数据却不对。这是最坑的一类,通常意味着程序逻辑有问题,或者使用了错误的参数,但这已经不是调度器能发现的了,本篇只做简单提及。
不同类型对应不同的排查入口,如果一上来就按“失败”去查,很容易被误导。比如说任务没启动,RUN_DETAILS里根本没记录,这时候查日志表是查不出东西的,需要换视角去看。
1.2 四步法:锁对象、查日志、读状态、做验证
我通常把Oracle Scheduler排障拆成四步,每一步都有明确目标,避免在排查过程中东一榔头西一棒子:
第一步,锁对象。先确定是哪个JOB出了问题,它在哪个容器(PDB/CDB)里,运行在哪个数据库实例上,是单任务、窗口任务、还是链式任务(Chain)。这决定了后续要查哪些视图。
第二步,查日志。Oracle Scheduler的运行时日志分布在DBA_SCHEDULER_JOB_LOG和DBA_SCHEDULER_JOB_RUN_DETAILS两张视图里,前者记录每次运行的调度行为,后者记录运行结果和错误信息。经验不足的人经常漏掉前者,导致错过“任务到底有没有被调度起来”这个关键信息。
第三步,读状态。结合DBA_SCHEDULER_JOBS的状态字段和DBA_SCHEDULER_RUNNING_JOBS,判断任务当前处在什么节点。是排队等待?正在运行?还是运行途中异常中断?
第四步,做验证。找到疑似原因后,不要急着改配置,先在测试环境或非业务时间手动执行一次任务,确认修复方案真的有效,再回生产操作。
提示:四步法的核心价值在于“不跳步”。很多时候任务卡死,直接改参数,结果任务依然卡死,就是因为跳过了日志分析这一步,没搞清楚真正原因。
2. 诊断地图:吃透数据字典视图与任务状态机
2.1 5张必知视图的职责分工
Oracle Scheduler的数据字典视图并不算多,但每张都有明确分工。平时排障最常用的5张视图,我整理了一个速查表:
| 视图 | 主要作用 | 关键字段 |
|---|---|---|
DBA_SCHEDULER_JOBS | 查看任务的当前状态、启停状态、下次运行时间 | STATE,ENABLED,NEXT_RUN_DATE,JOB_TYPE |
DBA_SCHEDULER_RUNNING_JOBS | 查看正在运行的任务及所在会话 | SESSION_ID,RUN_START_DATE,ELAPSED_TIME |
DBA_SCHEDULER_JOB_LOG | 查看每次调度的历史记录 | LOG_ID,OPERATION,STATUS |
DBA_SCHEDULER_JOB_RUN_DETAILS | 查看每次运行的详细结果、耗时、错误 | RUN_ID,RUN_DURATION,ERROR#,ADDITIONAL_INFO |
DBA_SCHEDULER_JOBS_RUN_DETAILS | 多租户环境下PDB级汇总信息 | 与上面类似,重点看CON_ID |
第一张是任务的“户口本”,第二张是任务的“实时体温”,第三、四张是任务的“历史档案”。此外,如果是外部作业或链表作业,还需要关注DBA_SCHEDULER_CREDENTIALS(凭据)、DBA_SCHEDULER_CHAINS(链)等视图。
这里特别提醒一点:DBA_SCHEDULER_RUNNING_JOBS只记录当前正在运行的任务,很多新手查不出记录就以为任务没跑,忽略了任务可能刚结束、日志还没刷新的窗口期。所以判断“任务到底跑没跑”,一定要结合JOB_LOG看最近一条调度记录的时间。
2.2 任务状态机与字段细节
Oracle Scheduler的任务状态机,看起来简单,但实际运行时有很多细节:
SCHEDULED:任务已启用,等待调度执行。RUNNING:任务正在执行。SUCCEEDED:任务执行成功。FAILED:任务执行失败。STOPPED:任务被手动或自动停止。DISABLED:任务被禁用,不会触发。RETRY SCHEDULED:任务将按配置重试。CHAIN_STALLED:链表任务停滞(链的某个环节卡住了)。
理解状态机,关键是理解“调度”和“执行”是两个阶段。日志表里的OPERATION字段会区分这两种动作:CREATE_JOB、ENABLE、DISABLE、RUN、RETRY_RUN等。比如说一个任务已经被调度器拉起(RUN),但会话在等待锁,这个状态在DBA_SCHEDULER_JOBS里就是RUNNING,在JOB_LOG里已经写了RUN,只有结合会话级视图才能发现它其实是在“空转”。
2.3 日志字段的“坑”:run_duration的单位与时间偏差
我看到太多人在RUN_DETAILS的RUN_DURATION字段上翻车。这个字段的类型是INTERVAL DAY TO SECOND,也就是说它显示单位是“天 小时:分钟:秒”,而不是秒。很多初学DBA直接拿这个值去对比,得出“任务跑了0小时31分钟”之类的结论,换算错位。
另外,ACTUAL_START_DATE和SCHEDULED_START_DATE之间的差值是“调度延误时间”。这个差值如果长期偏大,说明任务排队严重,或者上一轮任务还没结束导致下一轮被跳过,这才是真正的排查点。
再看CPU_USED字段,它显示的也是INTERVAL DAY TO SECOND类型。通过对比RUN_DURATION和CPU_USED,可以快速判断任务时间都耗在哪:如果两者接近,说明CPU密集;如果差距很大,说明存在大量等待(锁、I/O、网络),这对后续问题定性极其重要。
注意:
DBA_SCHEDULER_JOB_RUN_DETAILS只保留一段时间的记录,具体保留时长由JOB_LOG_RETENTION参数控制,生产环境里如果想留更长的历史做分析,一定要提前调整,并定期把历史数据归档到业务表里。
3. RUNNING状态卡死:排查与止损实操
3.1 卡死的判断标准
任务卡在RUNNING状态,是所有故障里最考验功力的,因为“还在跑”和“跑不动”在调度器层面看起来是一样的。我通常用三个维度判断:
- 持续时间:任务的历史平均耗时为10分钟,现在跑了2小时还没结束,基本可以高度怀疑卡住。
- 会话状态:通过
V$SESSION关联查询,看会话的WAIT_CLASS和EVENT。如果长期处于Idle或Application锁等待,就是有问题。 - 进度反馈:通过
V$SESSION_LONGOPS查看长时间操作的进度。如果SOFAR和TOTALWORK长时间不变,说明事务停滞。
这里要区分“卡死”和“慢任务”:有些报表任务就是设计为跑数小时,这种不能算卡死。我的判断标准就一句话:是否超出了可接受的业务时间窗口,且会话等待事件异常。
3.2 证据链收集
确认卡死后,第一时间收集证据,不要急着杀会话。需要收集的信息包括:
第一步,确认任务与会话的关联:
SELECT rj.job_name, rj.session_id, rj.run_start_date, s.status, s.event, s.wait_class, s.sql_id, s.blocking_session FROM dba_scheduler_running_jobs rj LEFT JOIN v$session s ON rj.session_id = s.sid WHERE rj.job_name = 'YOUR_JOB_NAME';重点看BLOCKING_SESSION字段。如果有值,说明任务一直在等另一个会话释放资源,这时候要顺着阻塞链往下查,看最终是什么会话占着资源不放。
第二步,记录程序当前执行的SQL和调用栈:
SELECT sid, serial#, sql_id, sql_child_number, event, seconds_in_wait FROM v$session WHERE sid = &会话ID;拿到SQL_ID后,去V$SQL里看具体执行的是哪条语句,判断是不是走到了错误的分支逻辑。
第三步,查看长时间操作进度:
SELECT sid, opname, target, sofar, totalwork, elapsed_seconds, time_remaining FROM v$session_longops WHERE sid = &会话ID;3.3 恢复操作:从stop_job到杀会话
证据收集完毕,该止损就止损。恢复操作有个优先级:
先用优雅方式停止任务:
EXEC dbms_scheduler.stop_job(job_name => 'YOUR_JOB_NAME', force => TRUE);很多人对FORCE => TRUE有误解,以为就是强制杀掉,其实它只是给会话发了一个停止信号。对于被锁阻塞的任务,它会等待锁释放超时后返回,但会话可能还活着。如果STOP_JOB执行后任务状态依然是RUNNING,就需要动用到会话级别:
ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;杀掉会话后,再检查任务状态:
SELECT job_name, state FROM dba_scheduler_jobs WHERE job_name = 'YOUR_JOB_NAME';此时状态应该变为STOPPED。注意,STOPPED和FAILED不一样,它表示是人为停止的,日志表里会记录STOPPED状态。
杀完会话后,还要确认是否有残留的外部进程。如果任务是EXTERNAL_SCRIPT类型,杀掉了数据库会话可能外部脚本还在操作系统层面跑,需要检查对应的OS进程。
提示:在执行
KILL SESSION之前,务必确认这个任务没有正在处理关键事务,否则会导致事务回滚和业务数据不一致。恢复业务优先,但也要有预案。
3.4 卡死的常见根因
根据我的经验,Oraacle Scheduler任务卡死,八成逃不出以下情形:
一是锁等待。任务程序里更新了某张表,被业务会话锁住,长时间不提交不释放,任务只能干等。排查方法是看BLOCKING_SESSION链路,找到源头会话,联系业务确认是否可以杀掉。
二是外部资源不可用。比如任务调用的接口、FTP服务器、数据库链接(DBLINK)所指向的远端库失去响应。数据库会话不报错,就在那里一直等网络超时,表面上就是RUNNING卡死。
三是程序死循环。某些PL/SQL逻辑在异常数据下会陷入循环,SQL查不到进度(因为没有大的DML),只有CPU飙升。这种要根据会话的SQL_ID和抓取ASH(历史会话活动)来分析。
四是过度并行造成的资源争抢。同一时间段多个任务同时跑,资源被耗尽,会话排队等CPU或等I/O。这类问题在V$SESSION里能看到大量enq:相关等待,需要结合资源管理器和job_queue_processes排查。
4. 任务失败类故障:日志解读与根因定位
4.1 run_details里的错误密码
任务失败后,第一时间应该看DBA_SCHEDULER_JOB_RUN_DETAILS的这几个字段:STATUS、ERROR#、ADDITIONAL_INFO。
SELECT job_name, status, error#, run_duration, actual_start_date, additional_info FROM dba_scheduler_job_run_details WHERE job_name = 'YOUR_JOB_NAME' ORDER BY log_date DESC FETCH FIRST 5 ROWS ONLY;ERROR#字段存的是ORA-错误编码,比如ORA-04068表示依赖对象状态无效,ORA-01403表示NO_DATA_FOUND,ORA-27486表示权限不足。ADDITIONAL_INFO字段则记录了详细的错误堆栈,通常包含了任务程序的具体报错位置。
注意一点:任务失败后,Oracle Scheduler不会自动把所有异常都写成FAILED。如果任务程序里自己捕获了异常并提交,任务状态可能还是SUCCEEDED,这就要靠业务侧数据校验来兜底了。
4.2 外部作业(EXTERNAL JOB)的常见炸点
外部作业是Oracle Scheduler里踩坑最多的一类。它执行的是操作系统命令、shell脚本或外部可执行程序,坑点主要在三个地方:
第一个是环境变量缺失。通过DBMS_SCHEDULER创建的外部作业,运行环境和你手动登录服务器时的环境变量完全不一样。PATH、ORACLE_HOME、JAVA_HOME这些统统需要显式在脚本里设置,否则脚本能创建但一运行就报ORA-27369(外部进程执行失败)或者找不到命令。
第二个是凭据问题。11g之后Oracle Scheduler推荐用CREDENTIAL来管理外部作业的操作系统账户。创建凭据:
BEGIN dbms_scheduler.create_credential( credential_name => 'APP_OS_CRED', username => 'oracle', password => 'xxxxxx' ); END; /然后把凭据赋予任务:
EXEC dbms_scheduler.set_attribute('YOUR_JOB_NAME', 'credential_name', 'APP_OS_CRED');如果没配置凭据,直接创建EXTERNAL_SCRIPT任务,会报ORA-27375: cannot run external job as user。
第三个是路径权限。脚本本身要有执行权限,目录要能被操作系统用户访问。很多外部作业故障,打开ADDITIONAL_INFO一看,其实就是shell脚本权限没给,或者脚本用了Windows换行符。
外部作业排查时,别只看数据库日志,还要去操作系统层面找日志。Oracle会为外部作业生成trace文件,通常位于$ORACLE_BASE/diag/rdbms/<实例名>/<实例名>/trace/目录下,文件名类似extjob_*.trc,这里面的信息往往比数据库日志更直接。
4.3 PL/SQL作业的权限、事务与NLS问题
PL/SQL类的调度任务,最常见的失败原因反而是权限。这里说的权限不只是执行存储过程的权限,更多是任务内部调用的对象权限。在SQL*Plus里当前用户能执行的存储过程,放到Scheduler任务里可能就报ORA-00942: table or view does not exist,原因就是Scheduler任务一旦用CURRENT_USER权限模式运行,就严格遵循对象所有者的权限,不继承创建任务用户的权限。
此外,PL/SQL任务还有一个隐患:隐式提交。Scheduler任务里的存储过程如果做了DDL操作(如TRUNCATE、CREATE TABLE),或者调用了DBMS_JOB旧版接口,可能产生隐式提交。一旦任务中途失败,数据一致性只能靠程序自己的事务处理来保证,调度器层面不会做回滚。
还有一个容易忽略的坑是NLS设置。Scheduler任务跑出来的日期格式、排序方式可能与SQL*Plus环境不一致,这会导致同样的程序,手动执行成功,调度执行失败。解决方法是在任务程序里显式设置NLS_LANG,或者在存储过程里使用ALTER SESSION SET NLS_...。
4.4 链式任务(CHAIN)的排查与重试
链式任务(Chain)是Oracle Scheduler里比较高级的功能,把多个步骤串成一条流水线。链式任务故障排查和单任务最大的不同点在于:不知道卡在哪个环节。
排查链条问题,先要看链的运行状态:
SELECT chain_name, run_id, state, current_step_name, error_message FROM dba_scheduler_chain_running_steps WHERE chain_name = 'YOUR_CHAIN_NAME';这张视图会告诉你链当前执行到哪个步骤、状态是什么。如果链处于CHAIN_STALLED状态,说明步骤运行失败或未定义下一步规则,需要结合链定义:
SELECT step_name, program_name, condition FROM dba_scheduler_chain_steps WHERE chain_name = 'YOUR_CHAIN_NAME' ORDER BY step_order;链的失败通常是规则写错或者程序返回结果不符合预期。比如ON_FAILURE规则没配,或者步骤的CONDITION条件表达式写错,都会导致链中断。另外,链式任务重试时用的是DBA_SCHEDULER_CHAIN_STEPS里的RETRY_COUNT和RETRY_DELAY属性,重试次数过多会掩盖真实报错,建议生产环境把重试次数控制在合理范围(我一般设置最多重试1到2次)。
5. 任务不按计划运行:调度侧排查要点
5.1 时区与开始时间设置
“任务为什么没按点跑”是另一类频繁出现的工单。症状各不相同,但根因常常出在时区上。
Oracle Scheduler的调度时间是基于start_date附带的时间信息。如果创建任务时只给了start_date,没指定repeat_interval,任务只会执行一次。如果指定了repeat_interval,但没有在start_date里显式设置时区,它会沿用数据库会话的时区。最典型的坑是:数据库服务器是UTC时区,业务期望北京时间8点执行,任务实际在UTC 8点(北京时间16点)执行了。
正确处理方式是创建任务时显式带上时区:
BEGIN dbms_scheduler.create_job( job_name => 'YOUR_JOB_NAME', job_type => 'PLSQL_BLOCK', job_action => 'BEGIN your_procedure; END;', start_date => TIMESTAMP '2025-01-01 08:00:00 Asia/Shanghai', repeat_interval => 'FREQ=DAILY; BYHOUR=8; BYMINUTE=0; BYSECOND=0', enabled => TRUE ); END; /repeat_interval的写法也要注意,FREQ=DAILY; BYHOUR=8; BYMINUTE=0; BYSECOND=0表示每天8点整,而FREQ=DAILY不加BYHOUR则是每天同一时刻,很多时候就因为这个细节差出8小时。
5.2 job_queue_processes与资源计划的双重约束
任务被调度了,但没有立即执行,日志里显示SCHEDULED状态一直不变,这时候要考虑两个层面的排队问题。
第一个是job_queue_processes参数。这是控制数据库可以同时运行多少个任务(包括DBMS_JOB和DBMS_SCHEDULER任务)的参数。如果当前运行中的任务数已经达到上限,新任务只能在队列里等待。11g、12c以及更高版本中,这个参数多数情况会自动调整,但如果你手动设置过很小的值,就很容易出现任务排队。
第二个是资源管理器(Resource Manager)。Oracle Scheduler任务可以被分配到一个JOB CLASS,而JOB CLASS可以关联到具体的资源消费者组。如果当前激活的资源计划对某些消费者组做了限制,任务会等待资源而不是直接运行。这种情况下查DBA_SCHEDULER_RUNNING_JOBS看不到任务,但查DBA_SCHEDULER_JOBS状态却是SCHEDULED,日志里也可能没有明显的错误记录。
排查这类问题,要结合起来看:
SELECT job_name, job_class, state FROM dba_scheduler_jobs WHERE job_name = 'YOUR_JOB_NAME'; SELECT job_class_name, resource_consumer_group FROM dba_scheduler_job_classes;再配合当前激活的资源计划:
SELECT name, is_top_plan FROM v$rsrc_plan WHERE is_top_plan = 'TRUE';我遇到过一次印象深刻的问题:任务每天凌晨3点跑,某天开始延迟到5点才执行,查了一圈发现是窗口(Window)开启了资源计划,把该任务所属消费者组的MAX_UTILIZATION_LIMIT降到了10%,任务只能抢磁盘上剩余的10%资源,导致实际执行时间被无限拉长。
5.3 权限管理:CREATE JOB 之外的授权细节
任务不执行的另一个隐蔽原因是权限。很多人只知道CREATE JOB权限,但没意识到Oracle Scheduler的权限体系比这复杂:
CREATE JOB:允许用户创建任务,但只能管理自己schema下的任务。CREATE ANY JOB:允许在任意schema下创建任务。CREATE EXTERNAL JOB:允许创建外部作业(操作系统脚本),这个权限需要额外授权。MANAGE SCHEDULER:允许管理调度器的全局属性、窗口、资源等。SCHEDULER_ADMIN角色:拥有上述大部分权限。
实战中最常见的权限问题,是用户用SYSDBA连接做测试,任务一切正常;切到应用账号执行时,却报ORA-27486: insufficient privileges。排查方法很简单:检查执行任务的schema是否被授予了必要权限,特别注意外部作业有没有CREATE EXTERNAL JOB权限。
还有一种情况:任务是用A用户创建的,但B用户需要查看或管理这个任务,如果没有被授予对象的相应权限,虽然能在ALL_SCHEDULER_JOBS里看到任务列表,但查不到运行日志细节。这种权限隔离在业务部门自建任务的场景尤其常见。
6. 实战复盘:一次生产任务卡死从告警到恢复
6.1 故障现象与初步判断
有一次生产库凌晨接到告警:核心报表任务RP_DAILY_SUMMARY运行超过4小时仍未结束,正常情况下这个任务30分钟内就完成。当时距离业务取数只剩2个小时,压力非常大。
我的第一步不是杀会话,而是先看任务当前状态:
SELECT job_name, state, enabled, run_start_date FROM dba_scheduler_jobs WHERE job_name = 'RP_DAILY_SUMMARY';状态是RUNNING,启动时间4小时前。再看DBA_SCHEDULER_RUNNING_JOBS,拿到了SESSION_ID = 156。接着用V$SESSION关联:
SELECT sid, serial#, event, wait_class, blocking_session, seconds_in_wait, sql_id FROM v$session WHERE sid = 156;关键信息出来了:EVENT为enq: TX - row lock contention,WAIT_CLASS为Application,BLOCKING_SESSION指向另一个会话82。这说明任务不是程序死循环,而是在等一张表的行锁。
6.2 取证与根因确认
顺着阻塞链查会话82:
SELECT sid, serial#, username, machine, program, sql_id, event, seconds_in_wait FROM v$session WHERE sid = 82;发现会话82是应用服务器的JDBC连接,程序名显示为某个报表前端,SQL_ID对应一条UPDATE ... WHERE ...语句,且这个会话SECONDS_IN_WAIT已经非常长。进一步查这条SQL的SQL文本,确认它更新的就是任务程序要读的订单状态表。
到这里根因基本明确了:白天应用侧有个事务长时间不提交,锁住了订单表的关键行,凌晨的调度任务一跑就卡在行锁等待上。为了确认,我用V$LOCK把锁的LMODE和REQUEST模式打出来,确认82持有TM/TX锁,156等待同一把锁。
6.3 恢复流程与复盘
由于82会话已经没有任何活动(属于僵尸事务),经过和业务确认后,我执行了:
ALTER SYSTEM KILL SESSION '82, serial#' IMMEDIATE;接着检查任务状态,仍然是RUNNING。这是因为杀掉了阻塞会话后,任务要重新获取锁并继续执行,需要一点恢复时间。几分钟后,任务顺利完成:
SELECT job_name, status, run_duration, actual_start_date FROM dba_scheduler_job_run_details WHERE job_name = 'RP_DAILY_SUMMARY' ORDER BY log_date DESC FETCH FIRST 1 ROWS ONLY;这次的教训很深刻:复盘时发现这个应用团队经常有长事务,白天就出现过类似锁等待,只是当时没阻塞调度任务,没人重视。后来我做了三件事:一是给这个任务配置了DBMS_SCHEDULER.SET_ATTRIBUTE的max_runs和max_failures,让连续失败快速告警;二是和开发约定,所有批量更新必须小事务分批提交,避免长事务;三是在凌晨调度前增加了一个前置检查任务,发现长时间未提交事务就告警。
7. 预防与巡检:让故障不再反复
7.1 日志保留策略与清理脚本
排障依赖日志,但日志不会永久保留。Oracle Scheduler的日志保留策略由全局属性JOB_LOG_RETENTION控制,默认通常保留30天。生产环境我建议调长到90天甚至更多:
EXEC dbms_scheduler.set_scheduler_attribute('JOB_LOG_RETENTION', '90 DAYS');同时要养成定期把RUN_DETAILS里的关键记录归档到普通业务表的习惯,否则调度器自身的清理机制会把历史记录清掉,等到要追溯问题时就无据可查了。
还有一个细节:调度器日志表如果长期不清理,会越来越大,DBA_SCHEDULER_JOB_LOG里记录了每次启停任务的记录,RUN_DETAILS里记录了每次运行的详细日志。Oracle有PURGE_LOG过程可以手动清理,但实际生产环境我建议通过定期归档让这两张表保持合适大小,不要轻易全表删除,否则会影响正在运行的排障查询。
7.2 几个值得长期监控的指标
监控做得不好,故障只能靠人肉发现。基于我的实践经验,这几个指标最值得长期盯:
- 任务失败率:单位时间内
FAILED的任务数量。突然增多,要么是批量权限变更,要么是外部依赖大规模不可用。 - 超长运行任务:运行时长超过历史平均值2倍,或者超过业务约定SLA的任务。
- 排队延时:
ACTUAL_START_DATE和SCHEDULED_START_DATE的差值如果持续变大,说明调度能力不足。 - 停滞的链式任务:
DBA_SCHEDULER_CHAIN_RUNNING_STEPS里有长时间不动的STALLED记录。 - 状态异常的任务:长期处于
DISABLED但业务上应该启用的任务,或长期RUNNING的会话。
7.3 巡检SQL示例
我日常巡检会固定跑几条SQL,效率很高。第一条查所有失败任务:
SELECT job_name, status, error#, actual_start_date, run_duration FROM dba_scheduler_job_run_details WHERE status = 'FAILED' AND log_date > SYSDATE - 1 ORDER BY log_date DESC;第二条查超时任务(以超过平均时长2倍为例):
SELECT r.job_name, r.run_duration, r.actual_start_date, j.state FROM dba_scheduler_job_run_details r JOIN dba_scheduler_jobs j ON r.job_name = j.job_name WHERE r.run_duration > ( SELECT AVG(run_duration) * 2 FROM dba_scheduler_job_run_details WHERE job_name = r.job_name ) AND r.log_date > SYSDATE - 1;第三条查当前失败次数最多的JOB:
SELECT job_name, COUNT(*) fail_count FROM dba_scheduler_job_run_details WHERE status = 'FAILED' AND log_date > SYSDATE - 7 GROUP BY job_name ORDER BY fail_count DESC;这几条SQL直接放进监控平台的定时任务里,能在故障影响业务前发出预警。如果监控平台只能调外部API,也可以把查询结果输出到普通日志表,由平台自动读取。
8. 高频问题速查表
| 现象 | 可能原因 | 排查视图/方法 | 处理建议 |
|---|---|---|---|
任务一直SCHEDULED,不执行 | job_queue_processes限制、窗口资源计划限制 | V$PARAMETER、DBA_SCHEDULER_RUNNING_JOBS | 调整参数,检查活跃窗口及资源计划 |
任务RUNNING但长时间无进度 | 锁等待、外部资源不可用、死循环 | V$SESSION关联阻塞链、V$SESSION_LONGOPS | 杀阻塞会话,优化程序逻辑 |
任务FAILED且ERROR#为27486 | 权限不足 | DBA_SCHEDULER_JOBS、角色和系统权限 | 授予CREATE ANY JOB或相应权限 |
外部作业报ORA-27369 | shell脚本路径、环境变量、权限 | OS日志、extjob trace文件 | 补全环境变量,检查脚本权限 |
| 任务不按预期时间执行 | 时区设置错误、repeat_interval写法问题 | DBA_SCHEDULER_JOBS的start_date和next_run_date | 显式指定时区,用标准FREQ写法 |
链式任务CHAIN_STALLED | 步骤规则配置错误,或某一步失败 | DBA_SCHEDULER_CHAIN_RUNNING_STEPS | 修正规则,配置合理的重试策略 |
| 任务日志表空间暴涨 | 日志保留期太长 | DBA_SCHEDULER_JOB_LOG大小 | 归档并调整JOB_LOG_RETENTION |
任务显示SUCCEEDED但数据有误 | 程序逻辑或参数问题,非调度器问题 | 业务数据校验 | 程序层增强日志与校验 |
写到这里,想起自己刚从开发转DBA时,第一次处理Scheduler任务卡死,因为没看BLOCKING_SESSION,傻乎乎地等了一个多小时,最后还是在导师提醒下才发现是行锁问题。后来凡是接到Scheduler任务故障,我都会先问自己三个问题:任务现在在等什么?等到什么时候算超时?超时了怎么恢复?这三个问题想清楚,排障基本不会跑偏。也希望这篇指南能帮你少走这些弯路,把任务故障变成一条有清晰路径的流水线。