关于oracle Scheduler的介绍——管理scheduler

简介: 整理自君三思博客:http://blog.itpub.net/7607759/viewspace-612220/ 三、使用Schedules   10g 中新推出的SCHEDULER可能确实会让很多初接触的朋友感觉晕头晕脑,相比之前的jobs,SCHEDULER中新增的概念太多。

整理自君三思博客:http://blog.itpub.net/7607759/viewspace-612220/

三、使用Schedules

  10g 中新推出的SCHEDULER可能确实会让很多初接触的朋友感觉晕头晕脑,相比之前的jobs,SCHEDULER中新增的概念太多。比如说jobs,仍然可以理解成之前版本中的jobs,不过功能更加强大(注意10g中也仍然可以使用普通jobs,这是废话,相信看本篇文章的朋友目前应该还是这样在用),比如说program,指的是运行的程序(把要做什么单提出来了),比如说schedule,我将其翻译为调度(job我翻译为任务),定义执行的频率或者说周期。

3.1  创建和管理Schedule s

  Schedule ,中文直译的话应该理解成调度,从名字来看,它是一个逻辑实体(逻辑,还实体,好矛盾),就是说当创建了schedule之后,数据库中就肯定存在这一对象,只不过这一对象是用来描述job的执行周期。

  创建schedule可以通过DBMS_SCHEDULER.CREATE_SCHEDULE过程,该过程支持的参数如下:

SQL> desc dbms_scheduler.create_schedule;

Parameter       Type                     Mode Default? 

--------------- ------------------------ ---- -------- 

SCHEDULE_NAME   VARCHAR2                 IN            

START_DATE      TIMESTAMP WITH TIME ZONE IN   Y        

REPEAT_INTERVAL VARCHAR2                 IN            

END_DATE        TIMESTAMP WITH TIME ZONE IN   Y        

COMMENTS        VARCHAR2                 IN   Y        

  各参数分别代表含意如下:

    • SCHEDULE_NAME :指定schedule名称,注意名称不能重复。
    • START_DATE :指定该调度的开始时间,可为空,当为空时表示该调度暂不起用。
    • REPEAT_INTERVAL :指定调度的执行频率或周期。
    • END_DATE :指定调度的结束时间,可为空,为空时就表示该调度将一直进行。
    • COMMENTS :注释信息。

  这其中,比较有技术含量的是REPEAT_INTERVAL参数,对于这个参数大家应该不会太陌生,因为前面介绍Jobs,也曾经提到过同名的参数,Schedules中的REPEAT_INTERVAL参数和Jobs中的REPEAT_INTERVAL参数功能完全相同,甚至参数格式也一模一样。

  REPEAT_INTERVAL 参数的语法结构要复杂的多。其中最重要的是FREQ和INTERVAL两个关键字。

      • FREQ 关键字用来指定间隔的时间周期,可选参数有:YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, and SECONDLY,分别表示年、月、周、日、时、分、秒等单位。
      • INTERVAL 关键字用来指定间隔的频繁,可指定的值的范围从1-99。

  比如说,当指定REPEAT_INTERVAL=>'FREQ=DAILY;INTERVAL=1';就表示每天执行一次,如果将INTERVAL改为7就表示每7天执行一次,效果等同于FREQ=WEEKLY;INTERVAL=1。

  下面,创建一个schedule,指定调度为每周一次的频率,执行脚本如下:

SQL> begin

  2  DBMS_SCHEDULER.CREATE_SCHEDULE (

  3    schedule_name     => 'my_first_schedule',

  4    start_date        => SYSDATE,

  5    repeat_interval   => 'FREQ=WEEKLY; INTERVAL=1',

  6    comments          => 'Every 1 weeks');

  7  END;

  8  /

PL/SQL procedure successfully completed.

  查询当前已经创建的schedules,可以通过*_SCHEDULER_SCHEDULES视图(含DBA_,ALL_,USER_),例如,查看当前用户拥有的schedules,执行语句如下:

SQL> select schedule_name,repeat_interval from user_scheduler_schedules;

SCHEDULE_NAME                  REPEAT_INTERVAL

------------------------------ ------------------------------

MY_FIRST_SCHEDULE              FREQ=WEEKLY; INTERVAL=1

  如果要修改schedule属性的话,也是使用DBMS_SCHEDULER.SET_ATTRIBUTE过程,该过程的调用方式前面已经多次演示过,这里就不再重复举例了,仅说明一点,对于schedule来说,能够修改的属性包括:REPEAT_INTERVAL、COMMENTS、END_DATE、START_DATE以及EVENT_SPEC。

  至于删除schedule,再简单不过,执行DBMS_SCHEDULER.DROP_SCHEDULE过程即可,例如:

SQL> EXEC DBMS_SCHEDULER.DROP_SCHEDULE('MY_FIRST_SCHEDULE');

PL/SQL procedure successfully completed.


3.2 Schedules调度Programs执行的Jobs

  通过schedule调度program的执行的job,看到这样的形容是不是让你彻底晕头了,就说明你还是没搞明白10g中SCHEDULERS特性管理的jobs的含意,让三思更直白地给你描述描述。10g版本中SCHEDULER将JOB分成了多个部分,program负责做什么,schedule负责啥时候做,job就简单了,一个字:做。

  前面几个小节,三思已经分别演示了创建管理Jobs,创建管理Programs以及创建和管理Schedules,下面我们通过实例来演示,如何创建通过schedule调度program的执行的job吧。

  首先,创建一个program,操作如下:

SQL> BEGIN

  2  DBMS_SCHEDULER.CREATE_PROGRAM (

  3     program_name           => 'my_program1',

  4     program_action         => '/bin/date',

  5     program_type           => 'EXECUTABLE',

  6     enabled                => TRUE);

  7  END;

  8  /

PL/SQL procedure successfully completed.

  通过上述语句,我们定义了一个program,执行操作系统命令date,并输入到dt.log文件中。

  接下来定义一个schedule,操作如下:

SQL> begin

  2  DBMS_SCHEDULER.CREATE_SCHEDULE (

  3    schedule_name     => 'my_first_schedule',

  4    start_date        => SYSDATE,

  5    repeat_interval   => 'FREQ= DAILY ; INTERVAL=1',

  6    comments          => 'Every 1 weeks');

  7  END;

  8  /

PL/SQL procedure successfully completed.

  定义调试为每周执行一次。此处repeat_interval可根据实现情况进行修改。

  最后,创建job,按照指定的schedule,执行program,操作如下:

SQL> BEGIN

  2  DBMS_SCHEDULER.CREATE_JOB (

  3     job_name           =>  'execOScmd',

  4     program_name       =>  'my_program1',

  5     schedule_name      =>  'my_first_schedule',

  6     enabled            =>  true);

  7  END;

  8  /

PL/SQL procedure successfully completed.

  创建job时,start_date,repeat_interval,job_action等均无须指定,因为这些参数将由program和schedule来控制。

  这样,操作完成后,ORACLE就会自动定时(当前设置为每周执行一次)program中定义的操作。

  要查看当前的执行情况,通过*_scheduler_job_run_details即可查询(*_scheduler_job_log也可以,不过该视图中信息不如detail中全面)。例如,查看刚刚创建的"EXECOSCMD"任务的执行情况,执行命令如下:

SQL> select log_id, log_date, status, additional_info

  2    from user_scheduler_job_run_details

  3   where job_name = 'EXECOSCMD'

  4  ;

    LOG_ID LOG_DATE             STATUS     ADDITIONAL_INFO

---------- -------------------- ---------- ------------------------------

     13760 17-AUG-09 02.47.53.7 SUCCEEDED

           34050 PM +08:00

  看完这个示例之后,你是否对10g中的SCHEDULER特性多了些了解呢?千万表自满,SCHEDULER特性的功能还多着哪,接着往下看


3.3 设置Repeat Interval

  Job 和Schedule中REPEAT_INTERVAL参数都是用来控制执行的频率或周期,虽然说周期是一个时间性概念,不过REPEAT_INTERVAL指定的时候并不是一个时间值,而是由一组关键字描述的时间。

  除了前面介绍Job和Schedule的REPEAT_INTERVAL参数时,提到该参数拥有FREQ以及INTERVAL两个关键字,其实除此之外,还有如BYMONTH、BYWEEKNO、BYYEARDAY、BYDATE等等参数,可以用来进行更精确的定义,比如通过BYMONTH关键字指定调度运行的月份,BYDAY指定调度在哪天运行等等。

  REPEAT_INTERVAL 参数的详细语法如下:

repeat_interval = regular_schedule | combined_schedule

==============================

regular_schedule = frequency_clause

[";" interval_clause] [";" bymonth_clause] [";" byweekno_clause]

[";" byyearday_clause] [";" bydate_clause] [";" bymonthday_clause]

[";" byday_clause] [";" byhour_clause] [";" byminute_clause]

[";" bysecond_clause] [";" bysetpos_clause] [";" include_clause]

[";" exclude_clause] [";" intersect_clause][";" periods_clause]

[";" byperiod_clause]

==============================  

combined_schedule = schedule_list [";" include_clause]

[";" exclude_clause] [";" intersect_clause]

frequency_clause = "FREQ" "=" ( predefined_frequency | user_defined_frequency )

predefined_frequency = "YEARLY" | "MONTHLY" | "WEEKLY" | "DAILY" | 

   "HOURLY" | "MINUTELY" | "SECONDLY"

user_defined_frequency = named_schedule

==============================

interval_clause = "INTERVAL" "=" intervalnum

   intervalnum = 1 through 99

bymonth_clause = "BYMONTH" "=" monthlist

   monthlist = monthday ( "," monthday)*

   month = numeric_month | char_month

   numeric_month = 1 | 2 | 3 ...  12

   char_month = "JAN" | "FEB" | "MAR" | "APR" | "MAY" | "JUN" |

   "JUL" | "AUG" | "SEP" | "OCT" | "NOV" | "DEC"

byweekno_clause = "BYWEEKNO" "=" weeknumber_list

   weeknumber_list = weeknumber ( "," weeknumber)*

   weeknumber = [minus] weekno

   weekno = 1 through 53

byyearday_clause = "BYYEARDAY" "=" yearday_list

   yearday_list = yearday ( "," yearday)*

   yearday = [minus] yeardaynum

   yeardaynum = 1 through 366

bydate_clause = "BYDATE" "=" date_list

   date_list = date ( "," date)*

   date = [YYYY]MMDD [ offset | span ]

bymonthday_clause = "BYMONTHDAY" "=" monthday_list

   monthday_list = monthday ( "," monthday)*

   monthday = [minus] monthdaynum

   monthdaynum = 1 through 31

byday_clause = "BYDAY" "=" byday_list

   byday_list = byday ( "," byday)*

   byday = [weekdaynum] day

   weekdaynum = [minus] daynum

   daynum = 1 through 53 /* if frequency is yearly */

   daynum = 1 through 5  /* if frequency is monthly */

   day = "MON" | "TUE" | "WED" | "THU" | "FRI" | "SAT" | "SUN"

byhour_clause = "BYHOUR" "=" hour_list

   hour_list = hour ( "," hour)*

   hour = 0 through 23

byminute_clause = "BYMINUTE" "=" minute_list

   minute_list = minute ( "," minute)*

   minute = 0 through 59

bysecond_clause = "BYSECOND" "=" second_list

   second_list = second ( "," second)*

   second = 0 through 59

bysetpos_clause = "BYSETPOS" "=" setpos_list

   setpos_list = setpos ("," setpos)*

   setpos = [minus] setpos_num

   setpos_num = 1 through 9999

==============================

include_clause = "INCLUDE" "=" schedule_list

exclude_clause = "EXCLUDE" "=" schedule_list

intersect_clause = "INTERSECT" "=" schedule_list

schedule_list = schedule_clause ("," schedule_clause)*

schedule_clause = named_schedule [ offset ]

named_schedule = [schema "."] schedule

periods_clause = "PERIODS" "=" periodnum

byperiod_clause = "BYPERIOD" "=" period_list

period_list = periodnum ("," periodnum)*

periodnum = 1 through 100

==============================

offset = ("+" | "-") ["OFFSET:"] duration_val

span = ("+" | "-" | "^") "SPAN:" duration_val

duration_val = dur-weeks | dur_days

dur_weeks = numofweeks "W"

dur_days = numofdays "D"

numofweeks = 1 through 53

numofdays = 1 through 376

minus = "-"

  这个语法形式看起来复杂无比,其实实用起来很简单,之所以看起来复杂,是因为其功能太过灵活(之前的三思系列笔记中,已经阐述过灵活与复杂的关系),这里不准备逐条解释每一个语法细节,下面将着重通过一些常用设置,希望能够更有助于广大同仁的理解。

  例如:设置任务仅在周5的时候运行:

REPEAT_INTERVAL => 'FREQ=DAILY; BYDAY=FRI';

REPEAT_INTERVAL => 'FREQ=WEEKLY; BYDAY=FRI';

REPEAT_INTERVAL => 'FREQ=YEARLY; BYDAY=FRI';

  上述三条语句虽然指定的关键字小有差异,不过功能相同。

  设置任务隔一周运行一次,并且仅在周5运行:

  • REPEAT_INTERVAL => 'FREQ=WEEKLY; INTERVAL=2; BYDAY=FRI';

  设置任务在当月最后一天运行:

  • REPEAT_INTERVAL => 'FREQ=MONTHLY; BYMONTHDAY=-1';

  设置任务在3月10日运行:

  • REPEAT_INTERVAL => 'FREQ=YEARLY; BYMONTH=MAR; BYMONTHDAY=10';

REPEAT_INTERVAL => 'FREQ=YEARLY; BYDATE=0310';

  上述两条语句功能相同。

  设置任务每10隔天运行:

  • REPEAT_INTERVAL => 'FREQ=DAILY; INTERVAL=10';

  设置任务在每天的下午4、5、6点时运行:

  • REPEAT_INTERVAL => 'FREQ=DAILY; BYHOUR=16,17,18';

  设置任务在每月29日运行:

  • REPEAT_INTERVAL => 'FREQ=MONTHLY; BYMONTHDAY=29';

  设置任务在每年的最后一个周5运行:

  • REPEAT_INTERVAL => 'FREQ=YEARLY; BYDAY=-1FRI';

  设置任务每隔50个小时运行:

  • REPEAT_INTERVAL => 'FREQ=HOURLY; INTERVAL=50';

  另外,你是否在怀念常规job中设置interval的简便,虽然功能较弱,但是设置操作非常简单,无须懊恼,其实SCHEDULER中的REPEAT_INTERVAL也完全可以按照那种方式设置,前面都说了,REPEAT_INTERVAL实际上是指定周期,直接指定一个时间值,当然也是周期喽。

  比如说,设置任务每天执行一次,也可以设置REPEAT_INTERVAL参数值如下:

  • REPEAT_INTERVAL => 'trunc(sysdate)+1'

  又比如设置任务每周执行一次:

  • REPEAT_INTERVAL => 'trunc(sysdate)+7'

  不过需要注意,这种方式仅用于创建SCHEDULER中jobs时使用,不能用于schedule。


四、使用Events

  Event直译对应的中文解释是指事件,不过单纯讲事件毕竟太抽象了,举个示例来形容吧。A(对应某个应用程序,或者是ORACLE中的进程)在干活时突然眉头一皱说道,不好,前方有情况,这可怎么办!这时,只见它认真想了想,过了一会儿脸上一喜说道:有了,俗话说早请示啊晚汇报,出现情况要找领导,赶紧给领导发消息呗!于是B(也是对应某个应用或ORACLE进程)就收到了一条A发过来的"前方有XX情况"的消息,这个过程就叫EVENT(含A发消息以及B接收消息)。

  SCHEDULER 中有两种触发EVENT的情况:

  • Scheduler 触发的Events:

    Scheduler 中触发的Events,一般是说当前schduler中job的状态发生修改,类似job启动,或者运行结束,或者达到运行时间等诸如此类的动作,都能够抛出一个EVENT,接收到EVENT的applicate就可以根据这些信息进行适当的处理。

    比如说,由于系统太过于繁忙,超出job启动时间后30分钟,job仍然没能顺利启动,那么这个时候,Scheduler就可以抛出一条EVENT给外部的应用,以便外部应用能够及时通知DBA,进行处理。

  • application 触发的Events:

    外部的应用也可以触发Events,并且由Scheduler来接收并处理这一类型的Events。所谓Scheduler处理EVENT就是指Scheduler启动相应的job来执行相关操作,这类job在创建时专门声明了event的处理,这样当接收到EVENT时,这类job就会启动。

  Scheduler 使用Oracle高级队列来抛出以及销毁Events。当抛出Schduler触发的Events时,Scheduler将消息入队到默认的event队列,application则通过检查该队列来处理Events。当抛出application触发的Events时,application将消息入队到处理job对应的队列中。

  下面我们也按照这两个类型来介绍Scheduler中的Events。

4.1 Scheduler抛出的Events

  前面说了,Scheduler抛出的Events一般是指job状态改变时触发的,那么是不是说只要job状态发生了改变,就会触发Events,其实并非如此,因为默认情况下,job是不触发Events的。

  Scheduler 中的job有一个属性叫raise_events,专门用来设置job触发Events的条件,该属性在CREATE_JOB时不能执行,因此默认情况下该属性不会赋值,自然也就不会触发EVENT。要设置raise_events属性,只能是在job创建完成后,通过SET_ATTRIBUTE过程修改job的raise_events属性。

  例如,修改前面创建的job-,启用raise_events属性,执行语句如下:

SQL> BEGIN

  2  DBMS_SCHEDULER.SET_ATTRIBUTE('INSERT_TEST_TBL', 'raise_events',  DBMS_SCHEDULER.JOB_ALL_EVENTS)

  3  END;

  4  /

PL/SQL procedure successfully completed.

  上述示例中指定的raise_events属性的属性值DBMS_SCHEDULER.JOB_ALL_EVENTS,就是抛出Events的触发条件。

  触发Events的有下列的类型,分别代表不同的操作:

  • job_started :JOB启动;
  • job_succeeded :JOB成功结束;
  • job_failed :JOB执行失败;
  • job_broken :JOB被置为BROKEN状态;
  • job_completed :JOB达到最大运行次数,或者运行的结束日期;
  • job_stopped :JOB被STOP_JOB过程置为停止执行的状态;
  • job_sch_lim_reached :Job的schedule达到限定值;
  • job_disabled :JOB被置于DISABLE状态;
  • job_chain_stalled :运行于chain的JOB被置于CHAIN_STALLED状态;
  • job_all_events :含上述提到的所有类型;
  • job_run_completed :由于Job运行出错、成功结束或被手动停止。

  起用raise_events后,Scheduler就会按照设定的触发条件,当达到触发条件时,即会抛出事件信息到SYS.SCHEDULER$_EVENT_QUEUE队列。

  例如,手动执行一次INSERT_TEST_TBL,看看是否向队列中记录信息,操作如下:

SQL> exec dbms_scheduler.run_job('INSERT_TEST_TBL');

PL/SQL procedure successfully completed.

  执行下列脚本,出队数据:

SQL> set serveroutput on

SQL> DECLARE

  2    l_dequeue_options    DBMS_AQ.dequeue_options_t;

  3    l_message_properties DBMS_AQ.message_properties_t;

  4    l_message_handle     RAW(16);

  5    l_queue_msg          sys.scheduler$_event_info;

  6  BEGIN

  7    l_dequeue_options.consumer_name := 'TEST';

  8

  9    DBMS_AQ.dequeue(queue_name         => 'SYS.SCHEDULER$_EVENT_QUEUE',

 10                    dequeue_options    => l_dequeue_options,

 11                    message_properties => l_message_properties,

 12                    payload            => l_queue_msg,

 13                    msgid              => l_message_handle);

 14    COMMIT;

 15

 16    DBMS_OUTPUT.put_line('event_type : ' || l_queue_msg.event_type);

 17    DBMS_OUTPUT.put_line('object_owner : ' || l_queue_msg.object_owner);

 18    DBMS_OUTPUT.put_line('object_name : ' || l_queue_msg.object_name);

 19    DBMS_OUTPUT.put_line('event_timestamp: ' || l_queue_msg.event_timestamp);

 20    DBMS_OUTPUT.put_line('error_code : ' || l_queue_msg.error_code);

 21    DBMS_OUTPUT.put_line('event_status : ' || l_queue_msg.event_status);

 22    DBMS_OUTPUT.put_line('log_id : ' || l_queue_msg.log_id);

 23    DBMS_OUTPUT.put_line('run_count : ' || l_queue_msg.run_count);

 24    DBMS_OUTPUT.put_line('failure_count : ' || l_queue_msg.failure_count);

 25    DBMS_OUTPUT.put_line('retry_count : ' || l_queue_msg.retry_count);

 26  END;

 27  /

event_type : JOB_STARTED

object_owner : TEST

object_name : INSERT_TEST_TBL

event_timestamp: 25-AUG-09 12.49.29.558758 PM +08:00

error_code : 0

event_status : 1

log_id :

run_count : 1

failure_count : 0

retry_count : 0

PL/SQL procedure successfully completed.

  从返回的信息可以看到,event的类型为JOB_STARTED,表示JOB启动。实际上job:INSERT_TEST_TBL执行一次至少会向队列中插入两条event信息,一条为JOB_STARTED,一条则为JOB_SUCCEEDED(也可能是JOB_FAILED),这里不详细演示,感兴趣的朋友不妨自行测试。

  • 提示:SYS.SCHEDULER$_EVENT_QUEUE队列基于SYS.SCHEDULER$_EVENT_QTAB队列表,因此查询SYS.SCHEDULER$_EVENT_QTAB也可以获取上述的信息。

  SYS.SCHEDULER$_EVENT_QUEUE 是一个固定队列,实际应用的过程中,DBA应该根据实际情况,将该表访问权限授予相关用户,以便顺利出队该队列中的events信息。

  另外,友情提醒,默认情况下Scheduler仅保留最近24小时的Events信息,如果希望修改该设置的话,可以通过SET_SCHEDULER_ATTRIBUTE过程,修改scheduler的event_expiry_time属性,该项属性的属性值以秒为单位。







SQL> EXEC DBMS_SCHEDULER.DROP_SCHEDULE('MY_FIRST_SCHEDULE');

PL/SQL procedure successfully completed.




相关文章
|
6月前
|
Oracle 关系型数据库 数据库
9-4 Oracle管理表空间和数据文件
9-4 Oracle管理表空间和数据文件
|
7月前
|
SQL 监控 Oracle
Oracle创建和管理表空间
Oracle创建和管理表空间
60 1
|
1月前
|
存储 Oracle 关系型数据库
Oracle系列之七:表的创建与管理
Oracle系列之七:表的创建与管理
|
4月前
|
运维 Oracle 关系型数据库
医院检验科LIS系统源码,oracle数据库、报告管理、质控管理
医院检验科LIS系统源码,oracle数据库、报告管理、质控管理
59 0
|
6月前
|
存储 Oracle 关系型数据库
9-6 Oracle 管理索引
9-6 Oracle 管理索引
|
8月前
|
缓存 Oracle 关系型数据库
使用序列(Sequence)在 Oracle 数据库中管理唯一标识符
使用序列(Sequence)在 Oracle 数据库中管理唯一标识符
111 0
|
存储 SQL Oracle
Oracle 用户、角色管理简介
Oracle 用户、角色管理简介
103 0
|
SQL Oracle 关系型数据库
在Oracle中,如何管理联机Redo日志组与成员?
在Oracle中,如何管理联机Redo日志组与成员?
166 0
|
Oracle 关系型数据库 BI
6-3 Oracle 表的管理-表复杂查询
学习了解6-3 Oracle 表的管理-表复杂查询。
|
Oracle 关系型数据库
6 Oracle 表的管理-表查询(重点)
利用scott用户存在的几张表(emp,dept)来演示如何使用select查询语句。