Oracle 自动任务调度
13.1 Oracle任务调度概述
在Oracle中任务调度指某一(组)执行程序在特定的时间被周期性的执行。Oracle把任务调度称为job(作业)。
Advanced Scheduler Concepts:(PPT-II-450)
13.2 理解以下概念:
1) Advanced SCHEDULER下的jobs
JOB翻译为作业,一个基本的JOB由两方面组成:program和schedule。JOB总体上可分为两大类,基于时间的JOB和基于事件的JOB。
在Oracle 10g之前,采用dbms_job程序包来完成任务调度的相关工作。在Oracle 10g之后,Oracle推出了功能更加强大的
dbms_scheduler来完成作业调度工作。
*考点:必须将JOB_QUEUE_PROCESSES 实例参数设置为>0的值,否则调度程序将无法运行,默认值为1000。如果有任何定义的、
活动的作业,那么总是运行作业队列协调器(后台进程cjq0)。
SQL> show parameter job_queue_processes
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
job_queue_processes integer 1000
SQL> select program from v$process where program like '%J%';
PROGRAM
------------------------------------------------
oracle@timran.localdomain (J000) //术语:job slave即job工作者,根据参数可以有1000个
oracle@timran.localdomain (CJQ0) //术语:Job Coordinator即job协调员
2) Advanced SCHEDULER下的program
Program指的是运行的程序。包括具体的要操作的执行代码,可选的是三种类型:
PL/SQL BLOCK : 标准的pl/sql代码块;
STORED PROCEDURE : 编译好的PL/SQL存储过程,或者Java存储过程,以及外部的c子程序;
EXECUTEABLE : ORACLE数据库之外的应用,比如操作系统命令等等。
3) Advanced SCHEDULER下的schedule
Scheduler有的资料翻译为调度,有的翻译成时间表,简单来说指的就是作业运行周期,即时间和频率。
总结一下:如果不考虑与资源计划的结合,Schedules(调度);Programs(程序);Jobs(任务)这三者之间到底是个什么关系?直白地来说
就是:program负责具体做什么(左膀),schedule负责什么时候做(右臂),job就简单了(联手左膀右臂)。11g的SCHEDULER提供了六种
建立Job的存储过程,可以将program、schedule、job分立创建,也可以混合创建,相互组合非常灵活。
浏览一下dbms_scheduler中的内容。
SQL> spool /tmp/sch.txt
SQL> desc dbms_scheduler
SQL> spool off
$more /tem/sch.txt
4)JOB CLASSES
相当于创建了一个job组,可以将那些具有相同特性的job,统统放到一个Job Classes中,在Job Classes中的Jobs可以指派优先级(1-5),
,默认优先级是3,而1的优先级最高,然后让Job Classes与资源计划器结合进行管理。
5)WINDOW
窗口指定了作业运行的起始时间(START DATE)、终止时间(END DATE),运行时间(DURATION),以及重复间隔(REPEAT INTERVAL)等,
它实际上扩展了SCHEDULER(时间表)的概念,所谓扩展是指窗口可以激活资源管理计划,当窗口打开时,数据库自动切换到相关的
resource plan上,此时这个resource plan便处于活动状态(ACTIVE)。
窗口对于job class也有着特殊的意义,通过job class和resource plan联系,这就使窗口下被激活的resource plan中所关联的job class中
的所有job被激活了,这些job会根据优先权调度运行。
6)JOB CHAIN
CHAIN可以被视做一组Programs的组合,举个简单的例子:运行PROGRAM:A以及PROGRAM:B,如果成功的话继续运行PROGRAM:C,
否则的话运行PROGRAM:D。Programs:A、B、C、D以及执行的逻辑关系就构成了一个最简单的CHAIN。
7)轻型作业
轻型作业不显示在DBA_SCHEDULER_JOBS视图中,因为轻型作业不是模式对象,与普通作业相比创建和删除轻型作业的开销非常小。
使用轻型作业有两种情形:1)作业成百上千次的批量执行,2)单个作业运行时间较短。
DBMS_SCHEDULER程序包可以使用最少的语法来创建轻量作业。轻量作业只有很少的参数可以指定:作业参数和计划。作业的其余
元数据(包括权限)都是从作业模板继承来的。轻量作业的模板必须是PL/SQL 块或存储过程。轻量作业必须使用DBMS_SCHEDULER
程序包在PL/SQL 中创建。JOB_STYLE参数在EM 中是不可见的。
例1,指定时间和频率的
dbms_scheduler.create_job(
job_name =>'test_ltwtjob1',
program_name =>'test_prog',
repeat_interval =>'freq=hourly',
end_date =>to_timestamp(sysdate+1),
job_style =>'lightweight',
例2,使用预定义计划
dbms_scheduler.create_job(
job_name =>'test_ltwtjob1',
program_name =>'test_prog',
shedule_name =>'test_sched'
job_style =>'lightweight',
以上两种方法,轻型作业的名字叫TEST_LTWTJOB1,程序test_prog就是它的模板,(这个模板就是指program_name是一个单独的模块),
job_style是轻型作业的关键字。
建立单一的轻型作业也许意义不大,另一种方法是通过PL/SQL块一次执行几百个轻量级作业的数组。
考点:轻型作业的指定的程序类型只能是PLSQL_BLOCK或者STORED_PROCEDURE。
8)基于事件(Event)的作业
SCHEDULER中有两种触发EVENT的情况:
Scheduler触发的Events
Scheduler 中触发的Events,一般是说当前schduler 中job 的状态发生修改,类似job 启动,或者运行结束,或者达到运行时间
等诸如此类的动作,都能够抛出一个EVENT,接收到EVENT 的application 就可以根据这些信息进行适当的处理。
Application触发的Events
外部的应用也可以触发Events,并且由Scheduler来接收并处理这一类型的Events。所谓Scheduler 处理EVENT 就是指Scheduler
启动相应的job来执行相关操作,这类job在创建时专门声明了event 的处理,这样当接收到EVENT 时,这类job就会启动。
建立和管理基于事件(Event)的作业的方法超出了OCP考试范畴,这里就不多说了。
13.3 例:
下面通过实例来演示,如何创建program、schedule、job,以便能初步理解三者间的关系。
我们使用分立创建三者的方法,先建一个测试表
SQL> create table scott.job_test1(my_date date);
第一步,创建一个名叫my_pro1的program,(带PL/SQL BLOCK)操作如下:
BEGIN
DBMS_SCHEDULER.CREATE_PROGRAM(
program_name =>'my_pro1',
program_action =>'begin
insert into scott.job_test1 values(sysdate);
commit;
end;',
program_type =>'PLSQL_BLOCK',
number_of_arguments =>0,
comments =>'insert sysdate into table',
enabled =>TRUE);
END;
/
PL/SQL procedure successfully completed.
通过上述语句,我们定义了一个program,类型是一个oracle匿名块,其动作是将输入sysdate插入到scott.job_test中。
前三个属性是不可以省略的,它们没有缺省值,必须指定,属性number_of_arguments是指定支持的参数个数,默认为0,
最多255,这里又置为0,强调此参数为0,因为program_type是PLSQL_BLOCK。要为progremm 添加参数需要使用
DEFINE_PROGRAM_ARGUMENT存储过程。
第二步:我们建立一个schedule,规定开始program的操作时间,及操作频率。这里定义了20秒执行一次。
BEGIN
dbms_scheduler.create_schedule(
repeat_interval => 'FREQ=SECONDLY;INTERVAL=20',
start_date => sysdate,
comments => 'Start Every 20 seconds',
schedule_name => 'my_sch1');
END;
/
此处REPEAT_INTERVAL参数的语法结构比较复杂。其中最重要的是FREQ和INTERVAL 两个关键字。
FREQ 关键字用来指定间隔的时间周期,可选参数有:YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, and SECONDLY,
分别表示年、月、周、日、时、分、秒等单位。
INTERVAL 关键字用来指定间隔的频繁,可指定的值的范围从1-99。
本例'FREQ=SECONDLY;INTERVAL=30表示每30秒钟执行一次,如果是'FREQ=DAILY;INTERVAL=1';就表示每天执行一次,
第三步:创建job,按照指定的schedule,执行program,创建job时,start_date,repeat_interval,job_action等均无须指定,因为这些参数
将由program和schedule来控制。操作如下:
BEGIN
dbms_scheduler.create_job(
job_name => 'my_job1',
program_name => 'my_pro1',
schedule_name => 'my_sch1',
comments => 'insert sysdate into table',
enabled => TRUE);
END;
/
这样,操作完成后,ORACLE就会自动定时(当前设置为30秒执行一次)program中定义的操作。
检查结果:
SQL> select * from scott.job_test1;
MY_DATE
-------------------
2012-11-22 15:56:13
2012-11-22 15:56:43
2012-11-22 15:57:13
......
可以通过一些视图查看JOB状态
DBA_SCHEDULER_JOBS
DBA_SCHEDULER_RUNNING_JOBS; //这两个都是查看当前job运行状态的(考点)
DBA_SCHEDULER_JOB_LOG
DBA_SCHEDULER_JOB_RUN_DETAILS
例如,查看刚刚创建的"MY_JOB1"任务的执行情况的细节,执行命令如下:
SQL>select log_id, log_date, status, additional_info from user_scheduler_job_run_details where job_name = 'MY_JOB1';
除了作业按照时间表自动运行,你也可以手工干预它们,举几个例子:
使能作业:
SQL> exec dbms_scheduler.enable('my_job1');
禁用作业:
SQL> exec dbms_scheduler.disable('my_job1');
删除作业:
SQL> exec dbms_scheduler.drop_job('my_job1');
运行作业:
SQL> exec dbms_scheduler.run_job('my_job1');
停止作业:
SQL> exec dbms_scheduler.stop_job('my_job1');
等等,很多类似的存储过程...
从管理角度看,你能创建JOB,也应该能修改JOB,即修改JOB的属性,由于JOB属性众多,Oracle把这部分工作独立出来作为一个
存储过程,这就是DBMS_SCHEDULER.set_attribute, 此过程可以修改JOB属性,这里仅举几个(也是考点):
1)JOB_TYPE:指定job执行的任务的类型
有四个可选值:'PLSQL_BLOCK', 'STORED_PROCEDURE', 'EXECUTABLE', and 'CHAIN'。
2)JOB_ACTION:指定job 执行的任务.这一参数所指定的值依赖于JOB_TYPE 参数中的值, 比如说JOB_TYPE设置为 'STORED_PROCEDURE',
那么本参数值中指定的可以是ORACLE 中的过程名。
3)LOGGING_LEVEL:指定对jobs 执行情况记录的日志信息级别。
SCHEDULER 管理的JOB 对任务的执行情况专门进行了记录,同时用户还可以选择日志中记录信息的级别,有下列三种选择:
DBMS_SCHEDULER.LOGGING_OFF:关闭日志记录功能;
DBMS_SCHEDULER.LOGGING_RUNS:对任务的运行信息进行记录;
DBMS_SCHEDULER.LOGGING_FULL:记录任务所有相关信息,不仅有任务的运行情况,甚至连任务的创建、修改等也均将记入日志。
4)AUTO_DROP:当为TRUE时,一旦job到达停止运行的时间,该job就会被自动删除,否则的话job仍然存在,不过状态被修改为COMPLETED。
5)RESTARTABLE:指定jobs运行出错后,能否适时重启,该参数默认情况下设置为FALSE,如果设置为TRUE,就表示当任务运行时出错,
下次运行时间点到达时仍会启动,并且如果运行仍然出错,会再重复,连接出错达到6次,该job 就会停止。
13.5 Job Class(类)和 Window(窗口)
job class包含着一组有相同特性的job, 而window是一个时间段,当系统时间进入到窗口时间段,则窗口打开,作业运行。
它们和资源计划的关联是个难点:一个job是怎样和资源计划联系起来的?
看图(PPT-II-452),通过两条线路将JOB和资源计划关联起来的,需要理解的是:
第一条线路:
创建一个job class,将该Job class绑定某个资源用户组,则这个Job_Class将服从该用户组的资源分配计划。
创建job,再通过set_attribute过程赋予相对优先权(1-5), 然后将job加入Job Class后,会根据设定的优先值从1-5顺序执行的,1首先被执行,默认是3。
第二条线路:
建立window时必须绑定一个resource plan,当系统时间进入window后,便激活了该resource plan。Window也可以设置优先权(high和low)
window的优先级的作用是:在窗口重叠的情况下如何选择哪个窗口打开,假定有两个窗口,设置为同一时间
打开则优先级为high的会打开,如果是同样的优先级,window时间长的打开,如果优先级相同,window时间长度也相同,
怎么办?则两个window都不打开,返还给原来活动的window。
window和schedule功能上相似,但Oracle设计让window和resource plan绑定,以便可以完成不同的resource plan的自动切换。
查看哪个窗口是活动的以及哪个资源计划与该窗口相关联,可以使用DBA_SCHEDULER_WINDOWS视图。
SQL>SELECT window_name,resource_plan,enabled,active FROM DBA_SCHEDULER_WINDOWS;
例;Job Class和Window在Scheduler框架内的作用
接续刚才的例子,把两条线路完成:
4)使用EM建立一个Job Class,名叫My_Class,将之前的MY_JOB1加入到My_Class(MY_JOB1默认加入的是DEFAULT_JOB_CLASS),再将My_Class关联OLTP组。
5)建立一个叫DAY_WIN的窗口,该窗口将激活DAYTIME计划
BEGIN
DBMS_SCHEDULER.CREATE_WINDOW(
window_name=>'"DAY_WIN"',
resource_plan=>'DAYTIME',
start_date=>systimestamp at time zone 'Asia/Shanghai',
duration=>numtodsinterval(1, 'minute'),
repeat_interval=>'FREQ=MINUTELY;INTERVAL=2',
end_date=>null,
window_priority=>'HIGH',
comments=>'');
END;
/
这将立即打开DAY_WIN窗口,每隔2分钟将再次打开该窗口,每次激活DAYTIME计划,duration的含义是打开窗口的持续时间。这里的1分钟是窗口Active状态为true可以持续1分钟。
| 2分钟 | 2分钟 |...
窗口再次打开间隔2分钟 |--------------- |---------------|...
窗口持续打开时间1分钟 |Active | 关闭 |Active| 关闭 |...
也就是说:当repeat_interval(间隔)>duration(持续时间)才有意义,Oracle不检查这两个时间单位的逻辑关系。
至此,一个job关联一个resource_plan的框架已经搭建起来了,4)和5)两条线路需要联动配合,实为关键。
6)改一下MY_JOB1的时间表,原来它关联MY_SCH1这个Schedule,现在让它关联DAY_WIN这个window,
即EDIT(MY_JOB1)-->Schedul Type-->Use_Pre_Define_Window-->DAY_WIN
改一下MY_JOB1的JOB CLASS,原来它关联DEFAULT_JOB_CLASS,现在让它关联MY_CLASS
测试一下:
SQL> alter system set resource_manager_plan=''; //关闭资源计划,现在没有资源计划激活。
SQL> select * from scott.job_test1;
可以看到原来是每半分钟插入一条记录,现在是每间隔2分钟插入一条记录。
SQL> show parameter resource_manager_plan
resource_manager_plan string SCHEDULER[0x115E7]:DAYTIME //DAYTIME资源计划被DAY_WIN自动激活了(保持1分钟,2分钟后再激活)。
OK!到此,整个例子基本完成了。
*考点:
1)当create job过程创建作业时,无法指派优先级,必须在后面使用API的set attribute过程。
2)作业A在其类中的优先级是1,作业B在另一个类的优先级是5,作业B处于resource plan中有更高的优先权的使用者组内,则B先于A被执行。