基於事件驅動的Oracle作業排程

realkid4發表於2012-01-29

 

對很多系統而言,作業排程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> GRANT AQ_ADMINISTRATOR_ROLE TO 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 Default Comments

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

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和排程器機制,我們可以方便的將這種型別作業加以實現。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/17203031/viewspace-715226/,如需轉載,請註明出處,否則將追究法律責任。

相關文章