今天和你們分享一下 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; /