基于事件驱动的Oracle作业调度


对很多系统而言,作业调度event, 'Scheduler');" target="_self">Scheduler是不可缺少的部分。大数据量集中批量处理、OLAP数据聚集都需要利用业务空闲时段(如夜间)进行处理。Oracle自身提供了较为可靠的运行作业调度器机制,为我们提供了现成的Scheduler组件。


调度作业有两种大类型:基于时间(Time-Based)和基于事件(Event-Based)。基于时间的调度作业顾名思义,就是设置特定的时间调度规则。依据时间规则在特定的时间点触发执行代码程序。例如:每天夜间22:00执行数据聚合操作,生成聚合数据。在Oracle中,大部分的作业都是这种类型。比如从10G出现的统计信息收集作业,就是规定在工作日夜间22:00开始进行的基于时间作业。


基于事件的作业调度则是依据特定的事件场景。比如:在应用程序发生故障的时候,启动数据清理程序,将中间数据结果还原。这样的作业,调度时间是不确定的,依据具体的业务和程序场景。而且,基于事件作业执行的过程中,要具有作业的特性,也就是作业执行代码执行和触发作业程序之间是异步执行关系。


在Oracle中,我们可以方便的时候dbms_scheduler包进行基于时间作业的定义。同样,我们可以借助Oracle消息队列的特性,来实现Event Based作业类型。


1、原理和环境准备


为了更好说明问题,笔者选择Oracle 10R2作为实验环境,并且构建一个新的用户schema环境。


SQL> show user;


User is "SYS"


SQL> select * from v$version;


BANNER


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


Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod


PL/SQL Release 10.2.0.1.0 - Production


CORE 10.2.0.1.0 Production


TNS for 32-bit Windows: Version 10.2.0.1.0 - Production


NLSRTL Version 10.2.0.1.0–Production


创建一个新用户testuser,并授予相应的系统和角色权限。


SQL> create user testuser identified by testuser ;


User created


SQL> alter user testuser quota unlimited on users;


User altered


SQL> grant connect to testuser;


Grant succeeded


SQL> grant create table to testuser;


Grant succeeded


SQL> grant create sequence to testuser;


Grant succeeded


SQL> grant create type to testuser;


Grant succeeded


SQL> GRANTAQ_ADMINISTRATOR_ROLETO testuser;


Grant succeeded


SQL> GRANT CREATE JOB TO testuser;


Grant succeeded


只有设置了AQ_ADMINISTRATOR_ROLE角色,才能使用Oracle的Advanced Queue组件功能。


原理上:我们要利用Oracle的Advanced Queue组件的功能。要求在特定的Event发生时,我们需要向队列中传入一个标记对象。Scheduler会根据特定的标记对象标识来调用特定的作业Job代码程序。这样就实现了基本的Event Based Job。


2、日志插入作业


我们希望实现一个功能,就是在特定事件发生的时候,会向数据表中插入一条记录。


首先,我们准备代码数据表。


SQL> conn testuser/testuser@ots;


Connected to Oracle Database 10g Enterprise Edition Release 10.2.0.1.0


Connected as testuser


SQL> create sequence seq_test;


Sequence created


SQL> create table test_log (id number not null, comments varchar2(100), created date);


Table created


SQL> alter table test_log add constraint pk_test primary key (id);


Table altered


我们创建了日志数据表。当触发事件的时候,直接向该数据表中插入一条记录。


3、配置调度作业


首先,需要定义一个类型type,用于向AQ中触发作业。该type相当于事件发生的信息单元。


SQL> create or replace type t_event_que_payload as object (event_name varchar2(30));


2 /


Type created


创建事件表,用来记录消息队列AQ中消息信息。


SQL> exec dbms_aqadm.create_queue_table(queue_table => 'event_queue_table',queue_payload_type => 't_event_que_payload',multiple_consumers => true,comment => 'Test Event Queue');


PL/SQL procedure successfully completed


使用dbms_aqadm方法create_queue_table中,两个最重要的参数:queue_table是创建消息表的名称,queue_payload_type则是规定了队列中存放对象的type类型。


执行后,的确创建了数据表event_queue_table。


SQL> desc event_queue_table;


Name     Type     Nullable DefaultComments


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


Q_NAME     VARCHAR2(30)     Y


MSGID RAW(16)


CORRID VARCHAR2(128) Y


PRIORITY NUMBER Y


(篇幅原因,有省略……)


下面需要创建队列对象,单独执行出队列名称和队列数据表名称。


SQL> exec dbms_aqadm.create_queue(queue_name => 'event_queue',queue_table => 'event_queue_table');


PL/SQL procedure successfully completed


SQL> select name, queue_table, qid, queue_type,user_comment from user_queues;


NAME     QUEUE_TABLE     QID     QUEUE_TYPE     USER_COMMENT


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


EVENT_QUEUE     EVENT_QUEUE_TABLE     111623     NORMAL_QUEUE


AQ$_EVENT_QUEUE_TABLE_E     EVENT_QUEUE_TABLE     111622     EXCEPTION_QUEUE exception queue


注意,为了队列AQ,Oracle要创建出多个数据表,用于进行不同的消息存储。同时,处于性能等多方面的考量,很多这样的数据表是采用IOT(Index-Organized Table)结构的。


SQL> select table_name, tablespace_name, iot_name,iot_type from user_tables;


TABLE_NAME     TABLESPACE_NAME     IOT_NAME     IOT_TYPE


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


TEST_LOG         USERS


EVENT_QUEUE_TABLE USERS


AQ$_EVENT_QUEUE_TABLE_S USERS


SYS_IOT_OVER_111613 USERS AQ$_EVENT_QUEUE_TABLE_G IOT_OVERFLOW


AQ$_EVENT_QUEUE_TABLE_I IOT


AQ$_EVENT_QUEUE_TABLE_G IOT


AQ$_EVENT_QUEUE_TABLE_H IOT


AQ$_EVENT_QUEUE_TABLE_T IOT


8 rows selected


最后,启动创建出的AQ队列event_queue。


SQL> EXEC DBMS_AQADM.start_queue (queue_name => 'event_queue');


PL/SQL procedure successfully completed


注意,此时队列状态开启为可用。


SQL> select name, queue_table, qid, queue_type,ENQUEUE_ENABLED, DEQUEUE_ENABLED from user_queues;


NAME     QUEUE_TABLE     QID     QUEUE_TYPE     ENQUEUE_ENABLED     DEQUEUE_ENABLED


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


EVENT_QUEUE     EVENT_QUEUE_TABLE     111623     NORMAL_QUEUE     YES     YES


AQ$_EVENT_QUEUE_TABLE_E EVENT_QUEUE_TABLE 111622 EXCEPTION_QUEUE NO NO


队列创建到此结束。下面创建作业,使用dbms_scheduler方法。


SQL> begin


2 dbms_scheduler.create_job(job_name => 'event_based_job',


3 job_type => 'PLSQL_BLOCK',


4 job_action => 'begin


5 insert into test_log values (seq_test.nextval, ''TT'', sysdate);


6 commit;


7 end;


8 ',


9 start_date=>systimestamp,


10 event_condition => 'tab.user_data.event_name = ''test_signal''',


11 queue_spec => 'event_queue',


12 enabled => TRUE);


13 end;


14 /


PL/SQL procedure successfully completed


SQL> select job_name, job_creator from user_scheduler_jobs;


        JOB_NAME         JOB_CREATOR


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


        EVENT_BASED_JOB         TESTUSER


注意包方法的几个主要参数。Job_name定义了新增加job的名称。Job_action定义了当这个Job被触发的时候,需要执行哪段代码。Event_condition规定了调度数据对象值为’test_signal’的时候才会执行这个作业。Queue_spec制定了监视事件的队列名称。


4、测试作业情况


我们模拟向队列中插入事件消息对象的场景。


SQL> DECLARE


2 l_enqueue_options DBMS_AQ.enqueue_options_t;


3 l_message_properties DBMS_AQ.message_properties_t;


4 l_message_handle RAW(16);


5 l_queue_msg t_event_que_payload;


6 BEGIN


7 l_queue_msg :=t_event_que_payload('test_signal'); --创建事件event消息对象;


8


9 DBMS_AQ.enqueue(queue_name =>'event_queue',


10 enqueue_options => l_enqueue_options,


11 message_properties => l_message_properties,


12 payload =>l_queue_msg,


13 msgid => l_message_handle);


14 COMMIT;


15 END;


16 /


PL/SQL procedure successfully completed


在实际使用的时候,只需要向AQ队列中插入消息体。Oracle就可以根据消息的内容调用特定的作业。观察结果:


--作业效果;


SQL> select * from test_log;


         ID     COMMENTS     CREATED


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


    1     TT             2012/1/29 1


--作业执行记录;


SQL> col job_name for a20;


SQL> select log_date, job_name, job_class, status from user_scheduler_job_log;


LOG_DATE     JOB_NAME      JOB_CLASS      STATUS


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


29-1月 -12     01.43.47.     EVENT_BASED_JOB     DEFAULT_JOB_CLASS     SUCCEEDED


484000     下午     +08:00


5、结论


event_based作业类型,在实际中出现的机率并不是很高。主要是一些数据现场恢复和清理工作。使用Oracle的AQ和调度器机制,我们可以方便的将这种类型作业加以实现。

你可能感兴趣的:(oracle)