oracle 10g起官方推荐用dbms_scheduler替代dbms_job,因后者缺乏状态跟踪、日志、依赖管理等功能,且在rac下易出错;dbms_job.submit需commit才生效,不支持可靠异步响应。
dbms_job 在 oracle 10g 及以后已不推荐使用
oracle 官方从 10g 开始明确建议用 dbms_scheduler 替代 dbms_job,因为后者缺乏对作业状态跟踪、日志、依赖、资源计划等支持,且无法跨数据库调度。很多团队仍在用 dbms_job 是因为旧系统迁移成本高,但新项目不应再选它。
如果你的数据库是 11g 或更高版本,DBMS_JOB 虽仍可用,但功能冻结,不修复 bug,也不新增特性。尤其在 RAC 环境下,DBMS_JOB 的实例绑定行为容易导致作业“消失”或重复执行。
用 DBMS_JOB 提交一个最简异步任务
核心是调用 DBMS_JOB.SUBMIT,注意它不直接执行,只注册作业到数据字典(USER_JOBS),由 CJQ 进程按调度拉起。常见错误是忘记提交事务,导致作业未持久化:
DECLARE
v_job NUMBER;
BEGIN
DBMS_JOB.SUBMIT(
job => v_job,
what => 'my_async_proc();',
next_date => SYSDATE,
interval => NULL -- 设为 NULL 表示只运行一次
);
COMMIT; -- ⚠️ 必须 COMMIT,否则作业不会生效
END;
-
what参数必须是完整可执行语句,末尾带分号;不能传变量或拼接 SQL 字符串(除非用动态 PL/SQL 块) -
next_date决定首次运行时间,设为SYSDATE并不等于“立即执行”,而是“尽快”,实际延迟可能达 60 秒(CJQ 扫描间隔) - 作业失败时默认重试(最多 16 次),错误堆栈仅存于
USER_JOBS.FAILURES,无详细日志
为什么 DBMS_JOB 不适合真正的“异步响应”场景
Web 或应用层调用存储过程后希望立刻返回,同时后台执行耗时逻辑——这看似符合 DBMS_JOB 的设计,但实际有严重缺陷:
- 作业 ID 无法可靠返回给调用方:
DBMS_JOB.SUBMIT输出参数job是会话局部的,若提交后连接断开,ID 就丢了 - 无法主动取消或查询进度:没有标准接口查某次提交的作业是否完成,
USER_JOBS.BROKEN和LAST_DATE字段更新滞后且不可靠 - 无上下文传递机制:不能把调用方的 session state、绑定变量、事务上下文带入作业,所有作业都在独立会话中运行,
AUTONOMOUS_TRANSACTION是唯一绕过方式,但会丢失事务一致性 - 若作业中抛异常且未捕获,整个作业会被标记为
BROKEN,后续不再触发,而调用方完全不知情
替代方案:DBMS_SCHEDULER 的最小可行改造
只需改几行就能获得可监控、可取消、带日志的异步能力。关键不是“换函数”,而是换抽象层级:
BEGIN
DBMS_SCHEDULER.CREATE_JOB(
job_name => 'MY_ASYNC_JOB_' || TO_CHAR(SYSDATE, 'YYYYMMDDHH24MISS'),
job_type => 'PLSQL_BLOCK',
job_action => 'BEGIN my_async_proc(); END;',
start_date => SYSTIMESTAMP,
repeat_interval => NULL,
enabled => TRUE,
auto_drop => TRUE
);
END;
-
job_name必须全局唯一,建议带时间戳或序列,避免冲突 -
auto_drop => TRUE避免作业执行完还残留元数据 - 作业状态查
DBA_SCHEDULER_JOB_LOG,错误详情在DBA_SCHEDULER_JOB_RUN_DETAILS - 取消作业用
DBMS_SCHEDULER.STOP_JOB('JOB_NAME'),比DBMS_JOB.REMOVE更可靠
真正难的不是语法,而是把“调用即返回”的期望,和数据库作业的异步本质对齐:你无法在存储过程中 await 一个 DBMS_JOB,只能接受“提交即承诺,查表知结果”。











