揭開job,scheduler,program,chain,job_class,window,window_group神祕面紗

pxbibm發表於2014-08-08

今天和大家分享一下

1.     作業(job),2.排程(scheduler),3.程式(program),4.鏈(chain),4.作業類(job_class),5.視窗(window),6.視窗組(window_group)

徹底揭開他們神祕的面紗。

在oracle 8i,9i中使用dbms_job方式留作業。其限制有二。
一是排程的時間不好控制,不太靈活,對時間的間隔難於把握。
二是不能排程作業系統的指令碼,只能排程資料庫內的程式或者語句。
隨著技術的發展,oracle在10g,11g 中推出了新的一代排程程式dbms_scheduler,這個程式克服了上面的兩個缺點。
下面我們介紹如何使用排程程式dbms_scheduler。

我們首先了解一下時間間隔的問題。
repeat_interval => 'FREQ=MINUTELY; INTERVAL=30'  
這句話的含義為:每30分鐘執行重複執行一次!
repeat_interval => 'FREQ=YEARLY; BYMONTH=MAR,JUN,SEP,DEC; BYMONTHDAY=30'  
這句話的含義為:每年的3,6,9,12月的30號執行job
一眼看上去格式有點亂,沒有章法,不如以前的時間間隔明白。因為我們不知道格式的含義。
日曆表示式基本分為三部分:
第一部分是頻率,也就是"FREQ"這個關鍵字,它是必須指定的;
第二部分是時間間隔,也就是"INTERVAL"這個關鍵字,取值範圍是1-999. 它是可選的引數;
最後一部分是附加的引數,可用於精確地指定日期和時間,它也是可選的引數,例如下面這些值都是合法的:
BYMONTH,BYWEEKNO,BYYEARDAY,BYMONTHDAY,BYDAY,BYHOUR,BYMINUTE,BYSECOND
repeat_interval => 'FREQ=HOURLY; INTERVAL=2' 每隔2小時執行一次job
repeat_interval => 'FREQ=DAILY'  每天執行一次job
repeat_interval => 'FREQ=WEEKLY; BYDAY=MON,WED,FRI"  每週的1,3,5執行job
既然說到了repeat_interval,你可能要問:"有沒有一種簡便的方法來得出,或者說是評估出job的每次執行時間,以及下一次的執行時間呢?"
dbms_scheduler包提供了一個過程evaluate_calendar_string,可以很方便地完成這個需求. 來看下面的例子:
set serveroutput on size 999999
declare
L_start_date TIMESTAMP;    --宣告需要的變數
l_next_date TIMESTAMP;
l_return_date TIMESTAMP;
begin
l_start_date := trunc(SYSTIMESTAMP); --取當前的時間
l_return_date := l_start_date;
for ctr in 1..10 loop      --迴圈10次
dbms_scheduler.evaluate_calendar_string(
'FREQ=DAILY; BYDAY=MON,TUE,WED,THU,FRI; BYHOUR=7,15',
l_start_date, l_return_date, l_next_date);
dbms_output.put_line('Next Run on: ' ||
to_char(l_next_date,'mm/dd/yyyy hh24:mi:ss')); --列印下次執行的時間
l_return_date := l_next_date;
end loop;
end;
/
結果如下:
Next Run on: 08/08/2014 07:00:00
Next Run on: 08/08/2014 15:00:00
Next Run on: 08/11/2014 07:00:00
Next Run on: 08/11/2014 15:00:00
Next Run on: 08/12/2014 07:00:00
Next Run on: 08/12/2014 15:00:00
Next Run on: 08/13/2014 07:00:00
Next Run on: 08/13/2014 15:00:00
Next Run on: 08/14/2014 07:00:00
Next Run on: 08/14/2014 15:00:00
 
我們看一下資料庫自己帶的排程的時間間隔。
col REPEAT_INTERVAL for a45
SQL>select job_name,repeat_interval from dba_scheduler_jobs;
JOB_NAME                       REPEAT_INTERVAL
------------------------------ ------------------------------------------
AUTO_SPACE_ADVISOR_JOB
GATHER_STATS_JOB
FGR$AUTOPURGE_JOB              freq=daily;byhour=0;byminute=0;bysecond=0
PURGE_LOG
RLM$SCHDNEGACTION              FREQ=MINUTELY;INTERVAL=60
RLM$EVTCLEANUP                 FREQ = HOURLY; INTERVAL = 1
 
已選擇6行。
我們發現有6個作業存在,但只有3個有時間的間隔。
很好理解,每天0點執行,其它的為間隔1小時執行,但為什麼有3個沒有時間間隔呢?
col JOB_NAME for a23
col SCHEDULE_NAME for a25
SQL> select JOB_NAME,REPEAT_INTERVAL,SCHEDULE_NAME from DBA_SCHEDULER_JOBS;
 
JOB_NAME                REPEAT_INTERVAL                               SCHEDULE_NAME
----------------------- --------------------------------------------- ------------------------
AUTO_SPACE_ADVISOR_JOB                                                MAINTENANCE_WINDOW_GROUP
GATHER_STATS_JOB                                                      MAINTENANCE_WINDOW_GROUP
FGR$AUTOPURGE_JOB       freq=daily;byhour=0;byminute=0;bysecond=0
PURGE_LOG                                                             DAILY_PURGE_SCHEDULE
RLM$SCHDNEGACTION       FREQ=MINUTELY;INTERVAL=60
RLM$EVTCLEANUP          FREQ = HOURLY; INTERVAL = 1
 
已選擇6行。
我們看到一個現象,有時間間隔的沒有排程的名稱,有排程名稱的就沒有時間間隔。
那什麼是排程呢?資料庫為常用的時間間隔編寫一個程式策略。叫做排程(scheduler)。
例如:
一個任務計劃執行的時間策略.比如我們想要建立一個晚上3點執行的任務計劃,就可以建立一個排程,凡是符合這個排程要求的,都可以呼叫這個我們預先建立好的排程.可以用dbms_scheduler.create_schedule來建立一個排程.
 
比如我建立一個名字叫MYTEST_SCHEDULE的排程,每天4:00執行.
Begin
  dbms_scheduler.create_schedule(
   repeat_interval => 'FREQ=DAILY;BYHOUR=4;BYMINUTE=0;BYSECOND=0',
  start_date => systimestamp at time zone 'PRC',
   comments => '---this is my test schedule---',
  schedule_name => 'MYTEST_SCHEDULE');
END;
/
 
上面我們看到PURGE_LOG的作業排程為DAILY_PURGE_SCHEDULE。
SQL> select REPEAT_INTERVAL from DBA_SCHEDULER_SCHEDULES where
 2 SCHEDULE_NAME='DAILY_PURGE_SCHEDULE';
 
REPEAT_INTERVAL
---------------------------------------------
freq=daily;byhour=3;byminute=0;bysecond=0
我們看到了該策略為每天3點執行。
但GATHER_STATS_JOB的排程為MAINTENANCE_WINDOW_GROUP,這又是什麼呢?
這是視窗組!
SQL> select * from DBA_SCHEDULER_WINGROUP_MEMBERS;
 
WINDOW_GROUP_NAME              WINDOW_NAME
------------------------------ -------------------
MAINTENANCE_WINDOW_GROUP       WEEKNIGHT_WINDOW
MAINTENANCE_WINDOW_GROUP       WEEKEND_WINDOW
我們看到維護視窗組MAINTENANCE_WINDOW_GROUP中有兩個視窗。
視窗又是什麼呢?
SQL> COL WINDOW_NAME FOR A20
SQL> COL REPEAT_INTERVAL FOR A79
SQL> SELECT WINDOW_NAME,REPEAT_INTERVAL FROM DBA_SCHEDULER_WINDOWS;
WINDOW_NAME          REPEAT_INTERVAL
-------------------- ---------------------------------------------------------------------
WEEKNIGHT_WINDOW     freq=daily;byday=MON,TUE,WED,THU,FRI;byhour=22;byminute=0; bysecond=0
WEEKEND_WINDOW       freq=daily;byday=SAT;byhour=0;byminute=0;bysecond=0
平時每天晚上10點執行,週六0點執行!
 
視窗(window):
 
  可以看成是一個更高功能的排程,視窗可以呼叫系統中存在的排程(也可以自行定義執行時間),而且,具有資源計劃限制功能,視窗可以歸屬於某個視窗組.
 
  可以使用DBMS_SCHEDULER.CREATE_WINDOW來建立一個視窗.
 
  例如我建立了一個名為mytest_windows_1的視窗,採用DAILY_PURGE_SCHEDULE的排程方式,資源計劃限制方案為 SYSTEM_PLAN,持續時間為4小時.
 
BEGIN
  DBMS_SCHEDULER.CREATE_WINDOW(
   window_name=>'mytest_windows_1',
   resource_plan=>'SYSTEM_PLAN',
   schedule_name=>'SYS.DAILY_PURGE_SCHEDULE',
   duration=>numtodsinterval(240, 'minute'),
   window_priority=>'LOW',
  comments=>'');
  END;
/
 
視窗組(window_group):
 
  一個/幾個視窗的集合.10g預設的自動採集統計資訊的排程就是一個視窗組的形式,譬如,設定兩個視窗,視窗一指定任務週日-----週五,晚上12點執行,而視窗二設定週六凌晨3點執行,這兩個視窗組成了一個視窗組,形成了這個job的執行排程策略.
 
  可以使用DBMS_SCHEDULER.CREATE_WINDOW_GROUP來建立一個視窗組.
BEGIN
  DBMS_SCHEDULER.CREATE_WINDOW_GROUP(
   group_name=>'mytest_window_group',
   window_list=>'MYTEST_WINDOWS_1,WEEKEND_WINDOW');
  END;
/
關於排程時間的問題我們搞清楚了,現在我們看一下排程的內容問題!
SQL> col PROGRAM_NAME for a40
SQL> select JOB_NAME,PROGRAM_NAME from DBA_SCHEDULER_JOBS;
 
JOB_NAME                PROGRAM_NAME
----------------------- --------------------------------------
AUTO_SPACE_ADVISOR_JOB AUTO_SPACE_ADVISOR_PROG
GATHER_STATS_JOB        GATHER_STATS_PROG
FGR$AUTOPURGE_JOB
PURGE_LOG               PURGE_LOG_PROG
RLM$SCHDNEGACTION
RLM$EVTCLEANUP
 
已選擇6行。
我們還研究GATHER_STATS_JOB這個作業,這個作業做什麼?是一個程式,叫做GATHER_STATS_PROG,GATHER_STATS_PROG內容是什麼呢?
SQL> select PROGRAM_ACTION from DBA_SCHEDULER_PROGRAMS
where PROGRAM_NAME='GATHER_STATS_PROG';
 
PROGRAM_ACTION
---------------------------------------------------------------------
dbms_stats.gather_database_stats_job_proc
啊,原來就是一個儲存過程。我們一步步的把排程的神祕面紗剝掉了!
現在我總結一下:有個程式GATHER_STATS_PROG,該程式呼叫了一個儲存過程。
有個視窗組MAINTENANCE_WINDOW_GROUP,其內含有平時和週末兩個策略。
我們的作業GATHER_STATS_JOB就是在MAINTENANCE_WINDOW_GROUP視窗組的時間內執行程式GATHER_STATS_PROG。搞的挺複雜,其實不難!
排程中還有兩個概念我們沒有講到。一個為鏈(chain),一個為作業類(job_class)。
鏈(chain):
  鏈可以看作是一個/幾個program/event scheduler的集合,為了維護需要,我們可能需要將很多不同的program放到一起依次執行,按照以前的模式,要麼將這幾個program能整合成一個大的整體,要麼分開幾個job來單獨執行,這無疑加重了維護負擔,而chain的出現,可以優化這個問題,我們將實現定義好的program集合到一起,然後統一制定一個job來執行,可以使用dbms_scheduler.create_chain來建立一個chain.
 
  比如,在我的系統中,我分別建立了一個EXECUTABLE型別的和一個STORED PROCEDURE型別的program,我需要他們順次執行,於是我可以這麼做:
 
BEGIN
  dbms_scheduler.create_chain( chain_name =>'MYTEST_CHAIN');
  dbms_scheduler.define_chain_step(chain_name =>'MYTEST_CHAIN', step_name =>'mytest_chain_1',program_name =>'P_1');
   dbms_scheduler.alter_chain(chain_name =>'MYTEST_CHAIN', step_name =>'mytest_chain_1',attribute=>'skip',value=>FALSE);
   dbms_scheduler.define_chain_step(chain_name =>'MYTEST_CHAIN',step_name =>'mytest_chain_2',program_name =>'P_2');
   dbms_scheduler.alter_chain(chain_name =>'MYTEST_CHAIN',step_name =>'mytest_chain_2',attribute=>'skip', value=>FALSE);
  dbms_scheduler.enable('MYTEST_CHAIN');
   END;
/
 
作業類(job_class):
 
  定義了執行作業的資源使用者組.通過使用視窗中的資源計劃,我們可以在不同資源組和不同作業類之間分配資源.可以使用 dbms_scheduler.create_job_class建立一個作業類.
BEGIN
  dbms_scheduler.create_job_class(
   logging_level => DBMS_SCHEDULER.LOGGING_RUNS,
   log_history => 100,
   resource_consumer_group => 'AUTO_TASK_CONSUMER_GROUP',
   job_class_name => 'MYTEST_JOB_CLASS');
  END;
/
排程的概念都講解完了,我們總結一下:
1.     作業(job),2.排程(scheduler),3.程式(program),4.鏈(chain),4.作業類(job_class),5.視窗(window),6.視窗組(window_group)
這些不是必須的。我們可以直接留作業,和以前dbms_job一樣,而且可以執行作業系統的指令碼。
begin
  dbms_scheduler.create_job
  (
  job_name => 'ARC_MOVE',
 repeat_interval => 'FREQ=MINUTELY; INTERVAL=30',
  job_type => 'EXECUTABLE',
  job_action => '/home/dbtools/move_arcs.sh',
  enabled => true,
  comments => 'Move Archived Logs to a Different Directory'
  );
  end;
/

 



pxboracle@live.com
2014.08.08 09:40
share you knowledge with the world. 


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

相關文章