一、简介:Moving from DBMS_JOB to DBMS_SCHEDULER
oracle中DBMS_JOB工具包的缺点:
①它只能够处理 PL/SQL 代码段 — 仅能处理匿名程序块和存储程序单元,它不能在数据库外部处理操作系统命令文件或可执行文件中的任何东西。
将不得不求助于操作系统调度实用工具(如 Unix 中的 cron 或 Windows 中的 AT 命令)。 或者,您可以使用甚至可能通过提供图形用户
界面来扩展这种功能的第三方工具。
②它只有在数据库启动并运行时才有效。如果数据库关闭,则作业不会运行。 数据库外部的工具必须人工检查数据库是否启动 — 而这可能很困难。
③手工管理时间间隔的繁琐
而10g增加的dbms_scheduler工具包所具有的特性提供了各方面的好处: 它是直接在数据库内部的一个作业调度实用程序,强大到足够处理所有类型的作业,而不只是 PL/SQL 代码段。 最好的一点是,它是数据库自带的,无需任何额外的成本。可以说是DBMS_JOB的一次飞跃。
二、利用dbms_scheduler创建简单的job
命名程序单元,将其指定为可执行文件
begin
dbms_scheduler.create_program
(
program_name => 'REPORT',
program_type => 'EXECUTABLE',
program_action => 'D:/SQL/main.sql',
enabled => TRUE,
comments => 'generate report'
);
end;
创建一个每天晚8点半运行一次的命名计划,该计划的名称为 EVERY_DAY
begin
dbms_scheduler.create_schedule
(
schedule_name => 'EVERY_DAY',
repeat_interval => 'FREQ=DAILY; BYHOUR=20; BYMINUTE=30',
comments => 'Every DATE-20:30'
);
end;
创建了程序和计划,然后把程序和计划连接起来成为一个job
BEGIN
DBMS_SCHEDULER.CREATE_JOB
(
JOB_NAME => 'REPORT_CUSTOMER',
PROGRAM_NAME => 'REPORT',
SCHEDULE_NAME => 'EVERY_DAY',
COMMENTS => 'GENERATE SIMPLE REPORT DAILY',
ENABLED => TRUE
);
END;
then,创建了一个名为report_customer的作业,它对应的脚本为一个main.sql文件,执行计划名为every_day。而事实上dbms_job也是面向对象的 只是面向的对象为PL/SQL程序,而对于shell脚本或者其他需要在操作系统里用命令行才能运行的东东是无能为力的。dbms_scheduler把所有可执行东西程序化,能够调用 OS 实用工具和程序,而不只是 PL/SQL 程序单元,包含有'PLSQL_BLOCK', 'STORED_PROCEDURE', 'EXECUTABLE'三类,shell脚本属于最后一类;把执行的时间点、间隔这些整成一个对象,下次可以直接使用,这是不是叫封装@@。而目前我们做的是shell脚本并不是数据库在管理调用,使用dbms_scheduler可以进行统一的管理。
当然,那么可能对于一个过程或者文件不想程序化,可不可以呢?
答案是肯定的,可以创建没有作业的程序。比如同样每天的晚八点半调用一个shell脚本。
begin
dbms_scheduler.create_job
(
job_name => 'MOVEDATA_TO_FX',
schedule_name => 'EVERY_DAY',
job_type => 'EXECUTABLE',
job_action => '/home/arup/dbtools/sync_sms_sent.sh',
enabled => true,
comments => 'sync data from xxt to fx'
);
end;
这里直接指定 OS 可执行文件,而无需首先将其创建为一个程序。 同样,您可以创建一个作业,而无需一个命名的计划。
begin
dbms_scheduler.create_job
(
job_name => 'MOVEDATA_TO_FX',
job_type => 'EXECUTABLE',
job_action => '/home/arup/dbtools/sync_sms_sent.sh',
repeat_interval => 'FREQ=DAILY; BYHOUR=20; BYMINUTE=30',
enabled => true,
comments => 'sync data from xxt to fx'
);
end;
至此,我们更清晰的看到了另一点优越性:能够以自然语言定义时间间隔,类似英语的一个表达式。而且ccreate_job有好几个重名的过程,这是不是叫做重载。
每隔三十分钟'FREQ=MINUTELY; INTERVAL=30'。
下面的例子很好的帮助我们说明这一问题:
假定您的生产应用程序在上午 7:00 和下午 3:00 变得最活跃,为了收集系统统计数据,您想从
星期一到星期五仅在上午 7:00 和下午 3:00 运行 Statspack。 如果您使用 DBMS_JOB.SUBMIT
来创建一个作业,那么 NEXT_DATE 参数将看起来像这样:
DECODE
(
SIGN
(
15 - TO_CHAR(SYSDATE,'HH24')
),
1,
TRUNC(SYSDATE)+15/24,
TRUNC
(
SYSDATE +
DECODE
(
TO_CHAR(SYSDATE,'D'), 6, 3, 1
)
)
+7/24
)
这种代码容易理解吗?实际上不容易。
REPEAT_INTERVAL 参数:
其他的一些时间间隔列举如下:
每周一至周五的早7点 下午三点:'FREQ=DAILY; BYDAY=MON,TUE,WED,THU,FRI; BYHOUR=7,15'
每月的最后一个星期天: FREQ=MONTHLY; BYDAY=-1SUN
每月的第三个星期五: FREQ=MONTHLY; BYDAY=3FRI
从每月底算起(而不是从每月初算起)的第二个星期五: FREQ=MONTHLY; BYDAY=-2FRI
如果想要验证时间间隔设置是否正确,那应该怎么办? 看看从日历字符串中构造的各个日期不是很好吗?
好的,可以使用 EVALUATE_CALENDAR_STRING 过程来预览接下来的日期的计算。
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
dbms_scheduler.evaluate_calendar_string(
'FREQ=DAILY; BYHOUR=20; BYMINUTE=30',
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: 11/15/2010 20:30:00
Next Run on: 11/16/2010 20:30:00
Next Run on: 11/17/2010 20:30:00
Next Run on: 11/18/2010 20:30:00
Next Run on: 11/19/2010 20:30:00
Next Run on: 11/20/2010 20:30:00
Next Run on: 11/21/2010 20:30:00
Next Run on: 11/22/2010 20:30:00
Next Run on: 11/23/2010 20:30:00
Next Run on: 11/24/2010 20:30:00
确认设置无误。
三、如何安排优先级
一个称职的好的作业调度系统必须支持为作业安排优先级的能力,但是DBMS_JOB工具包里没有哦。可以使用作业类、资源计划和调度程序窗口来
完成这一功能。
①定义一个资源使用组group
begin
dbms_resource_manager.clear_pending_area();
dbms_resource_manager.create_pending_area();
dbms_resource_manager.create_consumer_group (
consumer_group => 'report_group',
comment => 'generate report Group'
);
dbms_resource_manager.submit_pending_area();
end;
②定义一个资源计划
begin
dbms_resource_manager.clear_pending_area();
dbms_resource_manager.create_pending_area();
dbms_resource_manager.create_plan
('report_plan', 'generate report for business');
dbms_resource_manager.create_plan_directive(
plan => 'report_plan',
group_or_subplan => 'report_group',
comment => 'This is the report Plan',
cpu_p1 => 80, cpu_p2 => NULL, cpu_p3 => NULL, cpu_p4 => NULL,
cpu_p5 => NULL, cpu_p6 => NULL, cpu_p7 => NULL, cpu_p8 => NULL,
parallel_degree_limit_p1 => 4,
active_sess_pool_p1 => NULL,
queueing_p1 => NULL,
switch_group => 'OTHER_GROUPS',
switch_time => 10,
switch_estimate => true,
max_est_exec_time => 10,
undo_pool => 500,
max_idle_time => NULL,
max_idle_blocker_time => NULL,
switch_time_in_call => NULL
);
dbms_resource_manager.create_plan_directive(
plan => 'report_plan',
group_or_subplan => 'OTHER_GROUPS',
comment => NULL,
cpu_p1 => 20, cpu_p2 => NULL, cpu_p3 => NULL, cpu_p4 => NULL,
cpu_p5 => NULL, cpu_p6 => NULL, cpu_p7 => NULL, cpu_p8 => NULL,
parallel_degree_limit_p1 => 0,
active_sess_pool_p1 => 0,
queueing_p1 => 0,
switch_group => NULL,
switch_time => NULL,
switch_estimate => false,
max_est_exec_time => 0,
undo_pool => 10,
max_idle_time => NULL,
max_idle_blocker_time => NULL,
switch_time_in_call => NULL
);
dbms_resource_manager.submit_pending_area();
end;
③定义一个作业类
begin
dbms_scheduler.create_job_class(
job_class_name => 'report_jobs',
logging_level => dbms_scheduler.logging_full,
log_history => 45,
resource_consumer_group => 'report_group',
comments => 'report related jobs'
);
end;
④在定义作业的时候,使用作业类就达到了资源优先级控制的目的。
BEGIN
DBMS_SCHEDULER.CREATE_JOB
(
JOB_NAME => 'REPORT_CUSTOMER',
PROGRAM_NAME => 'REPORT',
SCHEDULE_NAME => 'EVERY_DAY',
COMMENTS => 'GENERATE SIMPLE REPORT DAILY',
ENABLED => TRUE,
JOB_CLASS =>'report_jobs',
AUTO_DROP =>FALSE
);
END;
其他的如:
①Removing a Job from the Job Queue
BEGIN
DBMS_SCHEDULER.DROP_JOB('REPORT_CUSTOMER');
END;
对应dbms_job.remove();
/
②Altering a Job
BEGIN
DBMS_SCHEDULER.SET_ATTRIBUTE(
name => 'myJOB1',
attribute => 'job_action',
value => 'INSERT INTO employees VALUES (7935, ''TOM'', ''DOGAN'',
''tom.dogan@xyzcorp.com'', NULL, SYSDATE, ''AD_PRES'', NULL,
NULL, NULL, NULL);');
END;
对应dbms_job.what()
③Creating a Job
对应dbms_job.submit()和dbms_job.isubmit()。
四、监视和管理
在一个作业启动之后,可以从 DBA_SCHEDULER_JOB_LOG 视图中监视它的状态,其中 STATUS 列显示了作业的当前状态。 如果它显示 FAILED,那么可以进一步向下查看,以从 DBA_SCHEDULER_JOB_RUN_DETAILS 视图中找出原因。
迄今为止,我们已经讨论了如何创建几种类型的对象: 程序、计划、作业类和作业。 如果想修改它们中的一些,以调整适应不断变化的需求,那该怎么办? 可以通过 DBMS_SCHEDULER 程序包中提供的 API 来实现这一目的。
参考:
http://www.oracle.com/technology/global/cn/pub/articles/10gdba/week19_10gdba.html
http://download.oracle.com/docs/cd/B12037_01/server.101/b10739/jobtosched.htm#i1033354