對很多系統而言,作業調度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 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和調度器機制,我們可以方便的將這種類型作業加以實現。