Oracle定时任务(2)-DBMS_JOB

Oracle 10g之前,可以使用dbms_job来管理定时任务。
10g之后,Oracle引入dbms_scheduler来替代先前的dbms_job,
在功能方面,它比dbms_job提供了更强大的功能和更灵活的机制/管理。

DBMS_JOB包
DBMS_JOB包中包含有许多过程和方法

名称 类型 描述
DBMS_JOB.ISUBMIT 过程

提交一个新任务,用户指定一个任务号
dbms_job.isubmit (
job       IN BINARY_INTEGER,
what      IN VARCHAR2,
next_date IN DATE,
interval  IN VARCHAR2 DEFAULT 'NULL',
no_parse  IN BOOLEAN DEFAULT FALSE);

*no_parse indicates whether to parse job PL/SQL at time of submission (FALSE) or execution (TRUE)
exec dbms_job.isubmit(100, 'begin MYPROC;end;', SYSDATE);

DBMS_JOB.SUBMIT 过程 提交一个新任务,系统指定一个任务号
dbms_job.submit(
JOB       OUT BINARY_INTEGER,
WHAT      IN  VARCHAR2,
NEXT_DATE IN  DATE           DEFAULT SYSDATE,
INTERVAL  IN  VARCHAR2       DEFAULT 'NULL',
NO_PARSE  IN  BOOLEAN        DEFAULT FALSE,
INSTANCE  IN  BINARY_INTEGER DEFAULT 0,
FORCE     IN  BOOLEAN        DEFAULT FALSE);
DBMS_JOB.REMOVE 过程 从队列中删除一个已经存在的任务
dbms_job.removejob IN BINARY_INTEGER);
DBMS_JOB.CHANGE 过程 更改用户设定的任务参数
dbms_job.change(
job       IN BINARY_INTEGER,
what      IN VARCHAR2,
next_date IN DATE,
interval  IN VARCHAR2,
instance  IN BINARY_INTEGER DEFAULT NULL,
force     IN BOOLEAN        DEFAULT FALSE);
exec dbms_job.change(100,NULL,NULL,'SYSDATE+3');
DBMS_JOB.WHAT 过程 更改PL/SQL任务定义
dbms_job.what (
job  IN BINARY_INTEGER,
what IN VARCHAR2);
DBMS_JOB.NEXT_DATE 过程 更改任务下一次运行时间
dbms_job.next_date(
job       IN BINARY_INTEGER,
next_date IN DATE);
DBMS_JOB.INTERVAL 过程 更改任务运行的时间间隔
dbms_job.interval (
job      IN BINARY_INTEGER,
interval IN VARCHAR2);
exec dbms_job.interval(100,'TRUNC(SYSDATE)+1');
DBMS_JOB.BROKEN 过程 将任务挂起,不让其重复运行,也可以重新启动任务。
dbms_job.broken (
job       IN BINARY_INTEGER,
broken    IN BOOLEAN,
next_date IN DATE DEFAULT SYSDATE);
exec dbms_job.broken(100,FALSE,NEXT_DAY(SYSDATE, 'MONDAY'));
DBMS_JOB.RUN 过程 在当前会话中立即执行任务。如果任务是broken状态,改为not broken.
dbms_job.run(
job   IN BINARY_INTEGER,
force IN BOOLEAN DEFAULT FALSE);
DBMS_JOB.INSTANCE 过程 用于RAC环境,为JOB的执行分配一个实例。
dbms_job.instance(
job      IN BINARY_INTEGER,
instance IN BINARY_INTEGER,
force    IN BOOLEAN DEFAULT FALSE);
select instance_number from gv$instance;
exec dbms_job.instance(100, 1);
DBMS_JOB.USER_EXPORT 过程 创建文字字符串,用于重新创建一个任务
dbms_job.user_export (
job    IN     BINARY_INTEGER,
mycall IN OUT VARCHAR2);
declare
  l_sqltxt varchar2(1000);
begin
  dbms_job.user_export(100,l_sqltxt);
  dbms_output.put_line(l_sqltxt);
end;

注意:执行上面的任何创建/删除/更改任务的过程后一定要马上commit。
          You must issue a COMMIT statement immediately after the statement.
          否则不仅会造成更改无效,甚至很有可能造成一些奇怪的现象,比如job停止之类,切记!

参数说明
DBMS_JOB包中所有的过程都有一组相同的公共参数。
1)job
参数job是一个整数,用来唯一地标示一个任务。
该参数既可由用户指定也可由系统自动赋予。
DBMS_JOB.SUBMIT过程通过获得序列SYS.JOBSEQ的下一个值来自动赋予一个任务号,
该任务号是作为一个OUT参数返回的,所以调用者随后可以识别出提交的任务。
DBMS_JOB.ISUBMIT过程则由调用者给任务指定一个识别号,
如果指定了重复任务号,ISUBMIT过程会抛出一个错误。
当数据库被导出或者被导入的时候,任务号也将被保留下来。
以在执行含有任务的数据的导入/导出操作时很可能会发生任务号冲突的现象。

2)what
what参数是一个可以转化为合法PL/SQL调用的字符串,该调用将被任务队列自动执行。
what参数的长度是4000个字节。
该参数的值一般情况下都是对一个PL/SQL存储过程的调用。PL/SQL调用必须用分号隔开。
还有一个实际经验就是最好将存储过程调用封装在一个匿名块中,这样可以避免一些比较莫名错误的产生。
例如,一般情况下,what参数可以这样引用:what =>’my_proc;’
但是比较安全的引用,应该这样写:what =>’begin my_proc; end;’
任何时候,我们只要通过更改what参数就可以达到更改任务定义的目的。
但是需要注意,通过改变what参数来改变任务定义时,用户当前的会话设置也被记录下来并成为任务运行环境的一部分。
如果当前会话设置和最初提交任务时的会话设置不同,就有可能改变任务的运行行为。

3)next_date
Next_date参数是用来调度任务队列中该任务下一次运行的时间。
这个参数对于DBMS_JOB.SUBMIT和DBMS_JOB.BROKEN这两个过程确省为系统当前时间,也就是说任务将立即运行。
当将一个任务的next_date参数赋值为null时,则该任务下一次运行的时间将被指定为4000年1月1日,
也就是说该任务将永远不再运行。
如果想在任务队列中保留该任务而又不想让其运行,将next_date设置为null是一个非常简单的办法。
next_date也可以设置为过去的一个时间。系统任务的执行顺序是根据它们下一次的执行时间来确定的,
于是将next_date参数设置为过去就可以达到将该任务排在任务队列前面的目的。

4)Interval
Internal参数是一个表示Oracle合法日期表达式的字符串。
这个日期字符串的值在每次任务被执行时算出,
强调一点,next_date是在一个任务开始时算出的,而不是在任务成功完成时算出的。
算出的日期表达式有两种可能,要么是未来的一个时间要么就是null。
当任务成功完成时,系统更新任务队列表,将下一次任务要运行的时间置为前面算出的next_date值。
当由interval表达式算出next_date是null时,任务自动从任务队列中移出,不会再继续执行。
因此,如果传递一个null值给interval参数,则该任务仅仅执行一次。


JOB_QUEUE_PROCESSES
Oracle有专门的后台进程来执行任务队列。
后台进程的数量通过JOB_QUEUE_PROCESSES来设置,可以设为0到1000。
*JOB_QUEUE_INTERVAL从9i之后就被废弃了。

需要注意,如果配置了最多1 000 个Jnnn进程,并不会看到真的有1 000 个进程随数据库启动。
相反,开始时只会启动一个进程,即作业队列协调器(CJQ0),
它在作业队列表中看到需要运行的作业时,会启动Jnnn 进程。
如果Jnnn 进程完成其工作,并发现没有要处理的新作业,此时Jnnn 进程就会退出。
unix下面可以用ps -ef | grep ora_j 查看这些进程(J000, ... J999),
另外还有一个ora_cjq0_xxx的队列管理进程。

作业队列协调器(CJQ0)进程被唤醒时,
它首先查看任务队列目录中所有的任务是否当前的时间超过了下一次运行的日期时间。
检测到需要该时间立即执行的任务后,这些任务按照下一次执行日期的顺序依次执行。
当开始执行一个任务时,其过程如下:

  1. 以任务所有者的用户名开始一个新的数据库会话。
  2. 当任务第一次提交或是最后一次被修改时,更改会话NLS设置和目前就绪的任务相匹配。
  3. 通过interval日期表达式和系统时间,计算下一次执行时间。
  4. 执行任务定义的PL/SQL
  5. 如果运行成功,任务的下一次执行日期(next_date)被更新,否则,失败计数加1。

任务运行失败后的重试
提交的任务如果运行失败会在2分钟后将再次试图运行该任务。
如果这次运行又失败了,下一次尝试将在4分钟后进行,再下一次在8分钟以后。
任务队列每次加倍重试间隔直到它超过了正常的运行间隔。
所以如果任务的运行间隔设置的比较短,比如1分钟,就观察不到这个重试时间间隔加倍现象。
在连续16次失败后,任务就被标记为中断的(broken),任务将不再重复执行。
这个时候,如果通过broken或者run等过程重新启动任务,成功之行一次后,这个失败间隔会清空。
如果重新启动后还是失败,那么失败次数变成17,job状态变回broken。


相关数据字典
任务队列中的任务信息可以通过以下几个字典视图来查看。

视图名 描述
DBA_JOBS 本数据库中定义到任务队列中的任务
DBA_JOBS_RUNNING 目前正在运行的任务
USER_JOBS 当前用户拥有的任务

DBA_JOBS 和USER_JOBS中的字段。

字段(列) 类型 描述
JOB NUMBER 任务的唯一标示号
LOG_USER VARCHAR2(30) 提交任务的用户
PRIV_USER VARCHAR2(30) 赋予任务权限的用户
SCHEMA_USER VARCHAR2(30) 对任务作语法分析的用户模式
LAST_DATE DATE 最后一次成功运行任务的时间
LAST_SEC VARCHAR2(8) 如HH24:MM:SS格式的last_date日期的小时,分钟和秒
THIS_DATE DATE 正在运行任务的开始时间,如果没有运行任务则为null
THIS_SEC VARCHAR2(8) 如HH24:MM:SS格式的this_date日期的小时,分钟和秒
NEXT_DATE DATE 下一次定时运行任务的时间
NEXT_SEC VARCHAR2(8) 如HH24:MM:SS格式的next_date日期的小时,分钟和秒
TOTAL_TIME NUMBER 该任务运行所需要的总时间,单位为秒
BROKEN VARCHAR2(1) 标志参数,Y标示任务中断,以后不会运行
INTERVAL VARCHAR2(200) 用于计算下一运行时间的表达式
FAILURES NUMBER 任务运行连续没有成功的次数
WHAT VARCHAR2(2000) 执行任务的PL/SQL块
CURRENT_SESSION_LABEL RAW MLSLABEL 该任务的信任Oracle会话符
CLEARANCE_HI RAW MLSLABEL 该任务可信任的Oracle最大间隙
CLEARANCE_LO RAW MLSLABEL 该任务可信任的Oracle最小间隙
NLS_ENV VARCHAR2(2000) 任务运行的NLS会话设置
MISC_ENV RAW(32) 任务运行的其他一些会话参数

DBA_JOBS_RUNNING的字段含义

数据类型 描述
SID NUMBER 目前正在运行任务的会话ID
JOB NUMBER 任务的唯一标示符
FAILURES NUMBER 连续不成功执行的累计次数
LAST_DATE DATE 最后一次成功执行的日期
LAST_SEC VARCHAR2(8) 如HH24:MM:SS格式的last_date日期的小时,分钟和秒
THIS_DATE DATE 目前正在运行任务的开始日期
THIS_SEC VARCHAR2(8) 如HH24:MM:SS格式的this_date日期的小时,分钟和秒


任务运行间隔指定
任务重复运行的时间间隔取决于interval参数中设置的日期表达式。

最简单的需求是特定的时间间隔后,重复运行该任务。例如天运行一次'sysdate+1'。
这样可能带来的问题是不能保证任务的下一次运行的准确时间。
比如第一次运行在午夜12点,这样以后每次运行理论上都在午夜12点。
但是如果某用户在上午8点使用run过程执行了该任务,以后该任务重新定时到上午8点运行。
另外如果数据库关闭或者说任务队列非常的忙以至于任务不能在计划的那个时间点准时执行,
都可能运行时间的不断“漂移”。

另一种需求是需要要任务在特定时间执行,下面是一些例子。
#每天午夜12点 'TRUNC(SYSDATE + 1)'
#每天早上8点30分 'TRUNC(SYSDATE + 1) + (8*60+30)/(24*60)'
#每星期二中午12点 'NEXT_DAY(TRUNC(SYSDATE ), ''TUESDAY'' ) + 12/24'
#每个月第一天的午夜12点 'TRUNC(LAST_DAY(SYSDATE ) + 1)'
#每季度的第一天凌晨1点 'TRUNC(ADD_MONTHS(SYSDATE,3),'Q') + 1/24'
#每个季度最后一天的晚上11点 'TRUNC(ADD_MONTHS(SYSDATE + 2/24, 3 ), 'Q' ) -1/24'
#每年1月1日凌晨1点执行 'add_months(trunc(sysdate,''yyyy''),12)+1/24'
#每星期六和日上午11点 'trunc(least(next_day(sysdate,7),next_day(sysdate,1)))+(11*60)/(24*60)'
  注:1-星期天 2-星期1 3-星期2 ... 7-星期6

你可能感兴趣的:(Oracle)