基於事件驅動的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.0Production

 

 

創建一個新用戶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 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和調度器機制,我們可以方便的將這種類型作業加以實現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章