全面学习ORACLE Scheduler特性2

四、使用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属性,该项属性的属性值以秒为单位。

4.2 Application抛出的Events

  首先要说明,这里所说的Application是个代词,即可以表示ORACLE数据库之外的应用程序,也可以是ORACLE数据库中的PROCEDURE等对象,总之你就将其理解成用户自己创建的对象就好了。

  Scheduler 能够抛出Events让外部应用处理,外部的应用也可以抛出Events让Scheduler启动job处理,不过并不是任何job都能够对外部应用抛出的Events做出响应,必须在创建jobs时明确指定响应的事件。那么如何指定呢?依靠下列两个附加的参数:

  • queue_spec :指定外部应用抛出的events消息入队的队列名;
  • event_condition :指定触发job启动的条件,这一参数的参数值在设置时应当基于事件消息的自身属性,因为事件消息在入队时,消息的属性都是由application定义的,因此在设置触发条件时,也应该根据这些属性值就行设置。

  下面,我们就演示创建一个由event触发启动的job,在此之前,首先需要进行一些准备工具,比如创建队列,由于队列需要基于一个队列表,因此在创建队列之前,首先要创建一个队列表,考虑到队列表需要依赖一个对象类型,因此在创建队列表之前,先得创建一个type.......复杂,具体的操作步骤如下,客官可要看仔细了:

SQL> create or replace type jss_type 1  as object

  2  (

  3    event_type         VARCHAR2( 1 0),

  4    object_owner       VARCHAR2( 30 ),

  5    object_name        VARCHAR2( 3 0)

  6  );

  7  /

Type created.

SQL> begin

  2    dbms_aqadm.create_queue_table(

  3      queue_table        => 'my_queue_tbl1',

  4      queue_payload_type => 'JSS_TYPE1',

  5      multiple_consumers => true);

  6  end;

  7  /

PL/SQL procedure successfully completed.

SQL> begin

  2    dbms_aqadm.create_queue(

  3      queue_name  => 'event_t1',

  4      queue_table => 'my_queue_tbl1');

  5  end;

  6  /

PL/SQL  procedure  successfully  completed.

  OK, 准备工作完成,下面就来创建一个event触发启动的job,创建脚本如下:

SQL> BEGIN

  2  DBMS_SCHEDULER.CREATE_JOB (

  3     job_name            =>  'EVENT_JOB_T1',

  4     job_type            =>  'STORED_PROCEDURE',

  5     job_action          =>  'P_INSERTINTOTEST',

  6     event_condition     =>  'tab.user_data.event_type = ''OP_INSERT''',

  7     queue_spec          =>  'EVENT_T1',

  8     enabled             =>  TRUE);

  9  END;

 10  /

PL/SQL  procedure  successfully  completed.

  上述脚本仅做演示,因此创建的job仍然执行P_INSERTINTOTEST过程。

  三思并不准备再编写一套外部的应用来触发,这里仅为了演示application触发job启动的示例,因此三思决定通过pl/sql直接向event_t1队列中添加消息的方式,触发job的启动,具体操作如下。

  首先要执行DBMS_AQADM.START_QUEUE过程,将event_t1置于允许入队和出队状态(默认情况下创建的队列是不允许出队和入队操作的),脚本如下:

SQL> exec dbms_aqadm.start_queue(queue_name => 'event_t1',enqueue => true,dequeue => true);

PL/SQL  procedure  successfully  completed.

  执行入队操作:

SQL> declare

  2    v_Message           jss_type1;

  3    v_EnqueueOptions    dbms_aq.enqueue_options_t;

  4    v_MessageProperties dbms_aq.message_properties_t;

  5    v_msg_handle        raw(16);

  6  begin

  7    v_message := jss_type1('OP_ SELECT ', user, 'tmpObj');

  8

  9    dbms_aq.enqueue(queue_name         => 'event_t1',

 10                    enqueue_options    => v_enqueueOptions,

 11                    message_properties => v_messageproperties,

 12                    payload            => v_message,

 13                    msgid              => v_msg_handle);

 14    commit;

 15

 16  end;

 17  /

PL/SQL  procedure  successfully  completed.

  查询队列表中的数据:

SQL> select user_data from my_queue_tbl1;

USER_DATA(EVENT_TYPE, OBJECT_OWNER, OBJECT_NAME)

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

JSS_TYPE1('OP_SELECT',  'TEST',  'tmpObj')

  然后查询job

SQL> select to_char(created,'yyyy-mm-dd hh24:mi:ss') from jss_1;

TO_CHAR(CREATED,'YY

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

2009-08-25  12:49:29

  看起来jss_1表中并未有新增加记录,似乎job没有执行啊。这很正常,还记得咱们创建job时指定的 event_condition 条件吗:

    6          event_condition          =>    'tab.user_data.event_type  =  ''OP_INSERT''',

  没错,只有当event_type为'OP_INSERT'时才会触发job的执行,前面入队时指定的是 OP_ SELECT ,当然没有触发job中指定的procedure啦,下面再次执行入队操作:

SQL> declare

  2    v_Message           jss_type1;

  3    v_EnqueueOptions    dbms_aq.enqueue_options_t;

  4    v_MessageProperties dbms_aq.message_properties_t;

  5    v_msg_handle        raw(16);

  6  begin

  7    v_message := jss_type1('OP_INSERT', user, 'tmpObj');

  8

  9    dbms_aq.enqueue(queue_name         => 'event_t1',

 10                    enqueue_options    => v_enqueueOptions,

 11                    message_properties => v_messageproperties,

 12                    payload            => v_message,

 13                    msgid              => v_msg_handle);

 14    commit;

 15

 16  end;

  17    /

  再次查看jss_1表看看:

SQL> select to_char(created,'yyyy-mm-dd hh24:mi:ss') from jss_1;

TO_CHAR(CREATED,'YY

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

2009-08-25 12:49:29

2009-08-25  13:21:21

  多了一条记录,说明job已经被自动触发。

  最后再补充一句,基于event的job不能通过DBMS_SCHEDULER.RUN_JOB过程执行,否则会触发ORA-00942: table or view does not exist错误。

五,使用链条

  今天要来认识一位新同学:CHAIN(注意不要敲成CHINA)。CHAIN可以被视做一组Programs的复合,举个简单的例子:运行PROGRAM:A以及PROGRAM:B,如果成功的话继续运行PROGRAM:C,否则的话运行PROGRAM:D。Programs:A、B、C、D以及执行的逻辑关系就构成了一个最简单的CHAIN。

  关于链的管理操作比较多,比如创建/删除/修改链,添加/修改/删除连锁步骤等等。

5.1创建链

5.1.1创建链对象

  

SQL> DESC dbms_scheduler.create_chain;

参数类型模式的默认? 

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

CHAIN​​_NAME VARCHAR2 IN            

在Y RULE_SET_NAME VARCHAR2        

在Y第二EVALUATION_INTERVAL INTERVAL DAY        

在Y的意见VARCHAR2        

  

SQL> EXEC dbms_scheduler.create_chain('my_chain1“);

PL / SQL过程已成功完成。

  定义好的链条,可以通过* _SCHEDULER_CHAIN​​S视图查看,例如:

SQL>从user_scheduler_chains的选择chain_name;

CHAIN​​_NAME

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

MY_CHAIN​​1

  步骤以及链中的规则。

5.1.2创建连锁步骤

  连锁步骤就是用来指定链执行的操作及执行步骤,创建链

SQL>开始

  2 DBMS_SCHEDULER.DEFINE_CHAIN​​_STEP(

  3 chain_name =>'my_chain1'

  4 step_name =>'my_step1'

  5 PROGRAM_NAME =>'p_p1');

  6月末;

  7 /

PL / SQL过程已成功完成。

  连锁步骤

  下面接着为my_chain1添加两个步骤,操作如下:

SQL>开始

  2 DBMS_SCHEDULER.DEFINE_CHAIN​​_STEP(

  3 chain_name =>'my_chain1'

  4 step_name =>'my_step2'

  5 PROGRAM_NAME =>'p_p2');

  6 DBMS_SCHEDULER.DEFINE_CHAIN​​_STEP(

  7 chain_name =>'my_chain1'

  8 step_name =>'my_step3“

  9 PROGRAM_NAME =>'p_p3');

 10日结束;

 11 /

PL / SQL过程已成功完成。

  要查询定义的连锁步骤,则是通过* _SCHEDULER_CHAIN​​_STEPS视图,例如:

SQL> SELECT chain_name,step_name,从user_scheduler_chain_steps PROGRAM_NAME;

CHAIN​​_NAME STEP_NAME PROGRAM_NAME

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

MY_CHAIN​​1 MY_STEP1 P_P1

MY_CHAIN​​1 MY_STEP2 P_P2

MY_CHAIN​​1 MY_STEP3 P_P3

5.1.3创建链的规则

  规则依赖于连锁步骤,每个链的规则都拥有的条件和动作属性,当满足条件时,则执行行动中指定的步骤。

  DBMS_SCHEDULER.DEFINE_CHAIN​​_RULE过程的语法如下:

SQL> DESC dbms_scheduler.define_chain_rule;

参数类型模式的默认? 

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

CHAIN​​_NAME VARCHAR2 IN            

条件VARCHAR2 IN            

行动VARCHAR2 IN            

在Y RULE_NAME VARCHAR2        

在Y的意见VARCHAR2        

  CHAIN​​_NAME

  • 为TRUE
  • 为FALSE
  • stepname []成功 
  • stepname []失败 
  • stepname []已停止 
  • stepname []完成 
  • stepname ERROR_CODE(整数,整数,整数... ...)
  • stepname ERROR_CODE IN(整数,整数,整数... ...)
  • stepname ERROR_CODE =整数
  • stepname ERROR_CODE!=整数
  • stepname ERROR_CODE <>整数
  • stepname ERROR_CODE>整数
  • stepname ERROR_CODE> =整数
  • stepname ERROR_CODE <整数
  • stepname ERROR_CODE <=整数

  甚至于,还可以制定成下列逻辑语法:

  • 表达和表达
  • 表达或表达
  • NOT(表达式)

  比如说,我们希望条件为第一步成功运行,那么可以指定条件参数值如下:

“(1)完成”

  行动

  例如,创建链

SQL> BEGIN

  2 DBMS_SCHEDULER.DEFINE_CHAIN​​_RULE(

  3 chain_name =>'my_chain1'

  4条件=>“真”,

  5行动=>'开始my_step1',

  6 rule_name =>'my_rule1');

  7 DBMS_SCHEDULER.DEFINE_CHAIN​​_RULE(

  8 chain_name =>'my_chain1“

  9条件=>'my_step1完成“,

 10行动=>'开始my_step2',

 11 rule_name =>'my_rule2');

 (12 DBMS_SCHEDULER.DEFINE_CHAIN​​_RULE

 13 chain_name =>'my_chain1'

 14条件=>'my_step2完成“,

 15行动0'=>'结束,

 16 rule_name =>'my_rule3');

 17尾;

 18 /

PL / SQL过程已成功完成。

5.1.4运行链条

  

SQL> BEGIN

  2 DBMS_SCHEDULER.RUN_CHAIN​​(

  3 chain_name =>'my_chain1'

  4 start_steps =>'my_step1');

  5月底;

  6 /

PL / SQL过程已成功完成。

  

SQL> SELECT从jss_t2;

TP的DT

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

p_p1插入03 - SEP - 09

p_p2插入03 - SEP - 09

  你看,jss_t2表中有了两条记录,对应前面设定的链式法则,说明my_step1和my_step2均已正确执行。

  

SQL> BEGIN

  2 DBMS_SCHEDULER.CREATE_JOB(

  3 JOB_NAME =>'chain_job_1'

  4工作类型=>'链',

  5 job_action =>'my_chain1'

  6 repeat_interval =>'FREQ =每日;间隔= 1',

  7启用=> TRUE);

  8月底;

  9 /

PL / SQL过程已成功完成。

5.2  管理Chains

5.2.1  修改Chains属性

  基本上碰到修改CHAIN属性的机率不会太大,因此确实没啥可修改的,对于CHAIN对象来说,能够修改的属性只有两个:evaluation_interval和comments,这两个参数一般情况下甚至都不会进行设置。如果你碰到了确实需要修改的情况,没问题,DBMS_SCHEDULER.SET_ATTRIBUTE过程还记的吧,没错,修改CHAIN也是用它。例如:

SQL> select chain_name,comments from user_scheduler_chains;

CHAIN_NAME           COMMENTS

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

MY_CHAIN1

SQL>  exec dbms_scheduler.set_attribute('my_chain1','comments','change it for a test!');

PL/SQL procedure successfully completed.

SQL> select chain_name,comments from user_scheduler_chains;

CHAIN_NAME           COMMENTS

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

MY_CHAIN1                        change  it  for  a  test  !

5.2.2  设置Chain Step运行属性

  修改Chain Step的运行属性就不能使用DBMS_SCHEDULER.SET_ATTRIBUTE了,而是有专门的过程DBMS_SCHEDULER.ALTER_CHAIN处理,该过程的定义如下:

SQL> desc dbms_scheduler.alter_chain;

Parameter  Type     Mode Default? 

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

CHAIN_NAME VARCHAR2 IN            

STEP_NAME  VARCHAR2 IN            

ATTRIBUTE  VARCHAR2 IN            

VALUE            BOOLEAN    IN          

  前两个参数就不说了,ATTRIBUTE参数用来指定STEP的属性值,可设定的属性值有3个,每个属性值都有TRUE和FALSE两个选项,由VALUE参数指定:

  • PAUSE :设置该参数值为TRUE时,当step运行时,其运行状态就会变更为PAUSED;
  • SKIP :设置该参数值为TRUE时,当step满足运行条件时,并不是执行step中的program,而是直接跳过,注意当SKIP参数值设置为TRUE,并且PAUSE参数值也被设置为TRUE,那么将会以PAUSE的状态优先;
  • RESTART_ON_RECOVERY :设置该参数值为TRUE时,如果由于数据库shutdown导致step被停止,那么当下次数据库启动时,step会自动重新运行。

  DBMS_SCHEDULER.ALTER_CHAIN 过程修改Chain Step属性后,只有当下次运行时才会生效,如果要修改当前运行中Chain Step的属性,也有一个专门的过程DBMS_SCHEDULER.ALTER_RUNNING_CHAIN进行处理,该过程语法与DBMS_SCHEDULER.ALTER_CHAIN一模一样,这里就不详细介绍了。

5.2.3  删除Chain Rules

  Chain Rules 没有对应的修改方法,如果要修改某个Chain的rule,只能首先删除不适当的rule,然后重新添加新rule(所谓添加,其实就是再重新定义一个rule)。

  删除Chain Rule有专门的过程DBMS_SCHEDULER.DROP_CHAIN_RULE,该过程语法如下:

SQL> desc dbms_scheduler.drop_chain_rule;

Parameter  Type     Mode Default? 

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

CHAIN_NAME VARCHAR2 IN            

RULE_NAME  VARCHAR2 IN            

FORCE            BOOLEAN    IN      Y                 

  三思一眼就能看出来,这个过程的调用方式那是相当简单,因此就不对各个参数详细介绍了,下面举个简单的示例,比如删除前面定义的my_rule3,执行过程如下:

SQL> exec dbms_scheduler.drop_chain_rule('my_chain1','my_rule3',true);

PL/SQL  procedure  successfully  completed.

5.2.4  删除Chain Steps

  删除Chain Step也有专门的过程DBMS_SCHEDULER.DROP_CHAIN_STEP进行处理,该过程语法如下:

SQL> desc dbms_scheduler.drop_chain_step;

Parameter  Type     Mode Default? 

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

CHAIN_NAME VARCHAR2 IN            

STEP_NAME  VARCHAR2 IN            

FORCE            BOOLEAN    IN      Y     

  看着有点儿眼熟是吧,没错,与drop_chain_rule的相似度高达90%以上。例如,删除之前定义的my_step3,执行过程如下:

SQL> exec dbms_scheduler.drop_chain_step('my_chain1','my_step3',true);

PL/SQL  procedure  successfully  completed.

5.2.5  删除Chains

  如果要删除Chain那就更简单了,执行dbms_scheduler.drop_chain过程即可,例如:

SQL> exec dbms_scheduler.drop_chain('my_chain1',true);

PL/SQL  procedure  successfully  completed.

  注意,执行drop_chain时,如果不指定force参数为TRUE,那么默认情况下ORACLE会首先检查要删除的CHAIN是否还有被依赖的对象,如果存在的话,会报ORA-27479错误,提示仍然有依赖的对象(所谓依赖的对象就是指,该chain仍然存在chain_step或chain_rule之类),因此无法直接删除。这种情况下解决方案有两种:一是手动删除所有相关的chain_step和chain_rule,然后再执行chain的删除,再就是附加force参数并指定参数值为true,这样ORACLE就会自动替你清除所有依赖的对象了。

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

##转自http://blog.sina.com.cn/s/blog_607cba070100wcvt.html

你可能感兴趣的:(dbms_scheduler)