Oracle 10g之前,可以使用dbms_job來管理定時任務。
10g之後,Oracle引入dbms_scheduler來替代先前的dbms_job,
在功能方面,它比dbms_job提供了更強大的功能和更靈活的機制/管理。
DBMS_JOB包
DBMS_JOB包中包含有許多過程和方法
| 名稱 |
類型 |
描述 |
| DBMS_JOB.ISUBMIT |
過程 |
提交一個新任務,使用者指定一個任務號 dbms_job.isubmit ( job IN BINARY_INTEGER, what IN VARCHAR2, next_date IN DATE, interval IN VARCHAR2 DEFAULT 'NULL', no_parse IN BOOLEAN DEFAULT FALSE); *no_parse indicates whether to parse job PL/SQL at time of submission (FALSE) or execution (TRUE) exec dbms_job.isubmit(100, 'begin MYPROC;end;', SYSDATE); |
| DBMS_JOB.SUBMIT |
過程 |
提交一個新任務,系統指定一個任務號 dbms_job.submit( JOB OUT BINARY_INTEGER, WHAT IN VARCHAR2, NEXT_DATE IN DATE DEFAULT SYSDATE, INTERVAL IN VARCHAR2 DEFAULT 'NULL', NO_PARSE IN BOOLEAN DEFAULT FALSE, INSTANCE IN BINARY_INTEGER DEFAULT 0, FORCE IN BOOLEAN DEFAULT FALSE); |
| DBMS_JOB.REMOVE |
過程 |
從隊列中刪除一個已經存在的任務 dbms_job.removejob IN BINARY_INTEGER); |
| DBMS_JOB.CHANGE |
過程 |
更改使用者設定的任務參數 dbms_job.change( job IN BINARY_INTEGER, what IN VARCHAR2, next_date IN DATE, interval IN VARCHAR2, instance IN BINARY_INTEGER DEFAULT NULL, force IN BOOLEAN DEFAULT FALSE); exec dbms_job.change(100,NULL,NULL,'SYSDATE+3'); |
| DBMS_JOB.WHAT |
過程 |
更改PL/SQL任務定義 dbms_job.what ( job IN BINARY_INTEGER, what IN VARCHAR2); |
| DBMS_JOB.NEXT_DATE |
過程 |
更改任務下一次已耗用時間 dbms_job.next_date( job IN BINARY_INTEGER, next_date IN DATE); |
| DBMS_JOB.INTERVAL |
過程 |
更改任務啟動並執行時間間隔 dbms_job.interval ( job IN BINARY_INTEGER, interval IN VARCHAR2); exec dbms_job.interval(100,'TRUNC(SYSDATE)+1'); |
| DBMS_JOB.BROKEN |
過程 |
將任務掛起,不讓其重複運行,也可以重新啟動任務。 dbms_job.broken ( job IN BINARY_INTEGER, broken IN BOOLEAN, next_date IN DATE DEFAULT SYSDATE); exec dbms_job.broken(100,FALSE,NEXT_DAY(SYSDATE, 'MONDAY')); |
| DBMS_JOB.RUN |
過程 |
在當前會話中立即執行任務。如果任務是broken狀態,改為not broken. dbms_job.run( job IN BINARY_INTEGER, force IN BOOLEAN DEFAULT FALSE); |
| DBMS_JOB.INSTANCE |
過程 |
用於RAC環境,為JOB的執行分配一個執行個體。 dbms_job.instance( job IN BINARY_INTEGER, instance IN BINARY_INTEGER, force IN BOOLEAN DEFAULT FALSE); select instance_number from gv$instance; exec dbms_job.instance(100, 1); |
| DBMS_JOB.USER_EXPORT |
過程 |
建立文字字串,用於重新建立一個任務 dbms_job.user_export ( job IN BINARY_INTEGER, mycall IN OUT VARCHAR2); declare l_sqltxt varchar2(1000); begin dbms_job.user_export(100,l_sqltxt); dbms_output.put_line(l_sqltxt); end; |
注意:執行上面的任何建立/刪除/更改任務的過程後一定要馬上commit。
You must issue a COMMIT statement immediately after the statement.
否則不僅會造成更改無效,甚至很有可能造成一些奇怪的現象,比如job停止之類,切記。
參數說明
DBMS_JOB包中所有的過程都有一組相同的公用參數。
1)job
參數job是一個整數,用來唯一地標示一個任務。
該參數既可由使用者指定也可由系統自動賦予。
DBMS_JOB.SUBMIT過程通過獲得序列SYS.JOBSEQ的下一個值來自動賦予一個任務號,
該任務號是作為一個OUT參數返回的,所以調用者隨後可以識別出提交的任務。
DBMS_JOB.ISUBMIT過程則由調用者給任務指定一個識別號,
如果指定了重複任務號,ISUBMIT過程會拋出一個錯誤。
當資料庫被匯出或者被匯入的時候,任務號也將被保留下來。
以在執行含有任務的資料的匯入/匯出操作時很可能會發生任務號衝突的現象。
2)what
what參數是一個可以轉化為合法PL/SQL調用的字串,該調用將被任務隊列自動執行。
what參數的長度是4000個位元組。
該參數的值一般情況下都是對一個PL/SQL預存程序的調用。PL/SQL調用必須用分號隔開。
還有一個實際經驗就是最好將預存程序調用封裝在一個匿名塊中,這樣可以避免一些比較莫名錯誤的產生。
例如,一般情況下,what參數可以這樣引用:what =>’my_proc;’
但是比較安全的引用,應該這樣寫:what =>’begin my_proc; end;’
任何時候,我們只要通過更改what參數就可以達到更改任務定義的目的。
但是需要注意,通過改變what參數來改變任務定義時,使用者當前的會話設定也被記錄下來並成為任務運行環境的一部分。
如果當前會話設定和最初提交任務時的會話設定不同,就有可能改變任務的運行行為。
3)next_date
Next_date參數是用來調度任務隊列中該任務下一次啟動並執行時間。
這個參數對於DBMS_JOB.SUBMIT和DBMS_JOB.BROKEN這兩個過程確省為系統目前時間,也就是說任務將立即運行。
當將一個任務的next_date參數賦值為null時,則該任務下一次啟動並執行時間將被指定為4000年1月1日,
也就是說該任務將永遠不再運行。
如果想在任務隊列中保留該任務而又不想讓其運行,將next_date設定為null是一個非常簡單的辦法。
next_date也可以設定為過去的一個時間。系統任務的執行順序是根據它們下一次的執行時間來確定的,
於是將next_date參數設定為過去就可以達到將該任務排在任務隊列前面的目的。
4)Interval
Internal參數是一個表示Oracle合法日期運算式的字串。
這個日期文字的值在每次任務被執行時算出,
強調一點,next_date是在一個任務開始時算出的,而不是在任務成功完成時算出的。
算出的日期運算式有兩種可能,要麼是未來的一個時間要麼就是null。
當任務成功完成時,系統更新任務隊列表,將下一次任務要啟動並執行時間置為前面算出的next_date值。
當由interval運算式算出next_date是null時,任務自動從任務隊列中移出,不會再繼續執行。
因此,如果傳遞一個null值給interval參數,則該任務僅僅執行一次。
JOB_QUEUE_PROCESSES
Oracle有專門的後台進程來執行任務隊列。
後台進程的數量通過JOB_QUEUE_PROCESSES來設定,可以設為0到1000。
*JOB_QUEUE_INTERVAL從9i之後就被廢棄了。
需要注意,如果配置了最多1 000 個Jnnn進程,並不會看到真的有1 000 個進程隨資料庫啟動。
相反,開始時只會啟動一個進程,即作業隊列協調器(CJQ0),
它在作業隊列表中看到需要啟動並執行作業時,會啟動Jnnn 進程。
如果Jnnn 進程完成其工作,並發現沒有要處理的新作業,此時Jnnn 進程就會退出。
unix下面可以用ps -ef | grep ora_j 查看這些進程(J000, ... J999),
另外還有一個ora_cjq0_xxx的隊列管理進程。
作業隊列協調器(CJQ0)進程被喚醒時,
它首先查看任務隊列目錄中所有的任務是否當前的時間超過了下一次啟動並執行日期時間。
檢測到需要該時間立即執行的任務後,這些任務按照下一次執行日期的順序依次執行。
當開始執行一個任務時,其過程如下: 以任務所有者的使用者名稱開始一個新的資料庫會話。 當任務第一次提交或是最後一次被修改時,更改會話NLS設定和目前就緒的任務相匹配。 通過interval日期運算式和系統時間,計算下一次執行時間。 執行任務定義的PL/SQL 如果運行成功,任務的下一次執行日期(next_date)被更新,否則,失敗計數加1。
任務運行失敗後的重試
提交的任務如果運行失敗會在2分鐘後將再次試圖運行該任務。
如果這次運行又失敗了,下一次嘗試將在4分鐘後進行,再下一次在8分鐘以後。
任務隊列每次加倍稍候再試直到它超過了正常的運行間隔。
所以如果任務的運行間隔設定的比較短,比如1分鐘,就觀察不到這個重試時間間隔加倍現象。
在連續16次失敗後,任務就被標記為中斷的(broken),任務將不再重複執行。
這個時候,如果通過broken或者run等過程重新啟動任務,成功之行一次後,這個失敗間隔會清空。
如果重新啟動後還是失敗,那麼失敗次數變成17,job狀態變回broken。
相關資料字典
任務隊列中的任務資訊可以通過以下幾個字典視圖來查看。
| 視圖名 |
描述 |
| DBA_JOBS |
本資料庫中定義到任務隊列中的任務 |
| DBA_JOBS_RUNNING |
目前正在啟動並執行任務 |
| USER_JOBS |
目前使用者擁有的任務 |
DBA_JOBS 和USER_JOBS中的欄位。
| 欄位(列) |
類型 |
描述 |
| JOB |
NUMBER |
任務的唯一標示號 |
| LOG_USER |
VARCHAR2(30) |
提交任務的使用者 |
| PRIV_USER |
VARCHAR2(30) |
賦予任務許可權的使用者 |
| SCHEMA_USER |
VARCHAR2(30) |
對任務作文法分析的使用者模式 |
| LAST_DATE |
DATE |
最後一次成功運行任務的時間 |
| LAST_SEC |
VARCHAR2(8) |
如HH24:MM:SS格式的last_date日期的小時,分鐘和秒 |
| THIS_DATE |
DATE |
正在運行任務的開始時間,如果沒有運行任務則為null |
| THIS_SEC |
VARCHAR2(8) |
如HH24:MM:SS格式的this_date日期的小時,分鐘和秒 |
| NEXT_DATE |
DATE |
下一次定時運行任務的時間 |
| NEXT_SEC |
VARCHAR2(8) |
如HH24:MM:SS格式的next_date日期的小時,分鐘和秒 |
| TOTAL_TIME |
NUMBER |
該任務運行所需要的總時間,單位為秒 |
| BROKEN |
VARCHAR2(1) |
標誌參數,Y標示任務中斷,以後不會運行 |
| INTERVAL |
VARCHAR2(200) |
用於計算下一已耗用時間的運算式 |
| FAILURES |
NUMBER |
任務運行連續沒有成功的次數 |
| WHAT |
VARCHAR2(2000) |
執行任務的PL/SQL塊 |
| CURRENT_SESSION_LABEL |
RAW MLSLABEL |
該任務的信任Oracle會話符 |
| CLEARANCE_HI |
RAW MLSLABEL |
該任務可信任的Oracle最大間隙 |
| CLEARANCE_LO |
RAW MLSLABEL |
該任務可信任的Oracle最小間隙 |
| NLS_ENV |
VARCHAR2(2000) |
任務啟動並執行NLS會話設定 |
| MISC_ENV |
RAW(32) |
任務啟動並執行其他一些會話參數 |
DBA_JOBS_RUNNING的欄位含義
| 列 |
資料類型 |
描述 |
| SID |
NUMBER |
目前正在運行任務的會話ID |
| JOB |
NUMBER |
任務的唯一標示符 |
| FAILURES |
NUMBER |
連續不成功執行的累計次數 |
| LAST_DATE |
DATE |
最後一次成功執行的日期 |
| LAST_SEC |
VARCHAR2(8) |
如HH24:MM:SS格式的last_date日期的小時,分鐘和秒 |
| THIS_DATE |
DATE |
目前正在運行任務的開始日期 |
| THIS_SEC |
VARCHAR2(8) |
如HH24:MM:SS格式的this_date日期的小時,分鐘和秒 |
任務運行間隔指定
任務重複啟動並執行時間間隔取決於interval參數中設定的日期運算式。
最簡單的需求是特定的時間間隔後,重複運行該任務。例如天運行一次'sysdate+1'。
這樣可能帶來的問題是不能保證任務的下一次啟動並執行準確時間。
比如第一次運行在午夜12點,這樣以後每次運行理論上都在午夜12點。
但是如果某使用者在上午8點使用run過程執行了該任務,以後該任務重新定時到上午8點運行。
另外如果資料庫關閉或者說任務隊列非常的忙以至於任務不能在計劃的那個時間點準時執行,
都可能已耗用時間的不斷“漂移”。
另一種需求是需要要任務在特定時間執行,下面是一些例子。
#每天午夜12點 'TRUNC(SYSDATE + 1)'
#每天早上8點30分 'TRUNC(SYSDATE + 1) + (8*60+30)/(24*60)'
#每星期二中午12點 'NEXT_DAY(TRUNC(SYSDATE ), ''TUESDAY'' ) + 12/24'
#每個月第一天的午夜12點 'TRUNC(LAST_DAY(SYSDATE ) + 1)'
#每季度的第一天淩晨1點 'TRUNC(ADD_MONTHS(SYSDATE,3),'Q') + 1/24'
#每個季度最後一天的晚上11點 'TRUNC(ADD_MONTHS(SYSDATE + 2/24, 3 ), 'Q' ) -1/24'
#每年1月1日淩晨1點執行 'add_months(trunc(sysdate,''yyyy''),12)+1/24'
#每星期六和日上午11點 'trunc(least(next_day(sysdate,7),next_day(sysdate,1)))+(11*60)/(24*60)'
注:1-星期天 2-星期1 3-星期2 ... 7-星期6