例行任务是单调和令人厌烦的。如果你以手动方式执行这些例行任务,则你不得不紧张地一次又一次地重复执行相同的任务,⑶一勾嬖谝恢址缦眨褪悄骋惶炷憧赡芑岷雎浴⒋砉湟桓霾街瑁蛘叻钙渌砦蟆racle数据库10g新的内置任务调度程序为你提供了调度例行任务的强大功能。利用这一调度程序,你可以规定要完成的工作、指定什么时候完成该工作,并监测该工作的完成情况,以便能纠正任何问题。你甚至可以控制分配给您所调度的任务的数据库资源和优先级,以确保首先完成最重要的工作,而不会对其它系统的活动产生不可抵抗的影响。
体系结构 你可以通过 DBMS_SCHEDULER 包或者通过Oracle企业管理器10g的数据库控制功能来访问这一新的调度程序。图1给出了与任务的创建和执行具有最直接关系的调度程序组件。一项任务将一个程序和一个调度表结合在一起。该程序定义将要运行的内容。例如,一个程序可以是一个 PL/SQL 块、一个存储过程,或者一个操作系统脚本。而调度表则定义什么时候运行该程序。对于一个一次性任务,调度表中仅包含一个起始时间。对于一个重复性任务,你可以指定一个起始时间,一个重复运行调度表,还可以根据需要指定结束时间。每次运行一项任务都被看作是一个任务实例。在开始使用该程序调度程序时必须掌握的三个核心组件就是任务、调度表和程序。
如图1所示,任务类将调度系统与资源管理系统连接起来,使你可以控制如何将数据库资源分配给正在运行的任务。利用任务类将一个任务下达到资源使用者组,该组是一个共享 CPU 时间、并行操作和其它资源分配的会话期组。然后,你可以利用资源计划来控制将这些资源分配给这个使用者组或其它使用者组。图2 显示出调度程序窗口如何控制不同资源计划在何时被激活。图2 还显示一个窗口组,它将类似的窗口组合成一个单一的实体。窗口和窗口组使你可以在很好地控制如何将数据库资源分配给不同的任务类。
图1:调度程序核心组件
图2:调度程序资源管理 开始 要创建并运行任务,你需要至少具有CREATE JOB 系统权限。如果你是一位 DBA,那么你将通过授予DBA 角色的SCHEDULER_ADMIN角色而具有 CREATE JOB权限和所有其它调度程序权限。你可以将 CREATE JOB 权限授予数据库用户,允许他们在其自己的模式中创建和运行他们自己的任务。例如,下面的语句将 CREATE JOB 授予用户 gennick:
GRANT CREATE JOB TO gennick;
与你应用DBMS_JOB时的情况不同,你不需要设置一个初始化参数,以启动一个任务协调程序后台进程。如果你使用该新的调度程序创建任务,则你的实例会在需要时自动启动一个任务协调程序后台进程。
如果你计划使用资源计划和使用者组来控制数据库资源的分配,则将你实例的RESOURCE_LIMIT 参数设置为TRUE。可以通过 ALTER SYSTEM完成该项设置,如下所示:
ALTER SYSTEM SET RESOURCE_LIMIT = TRUE;
最后,如果您将CREATE JOB 权限授予了一个非DBA用户,而且你希望该用户能够使用数据库控制的图形用户界面来调度任务,那么你还需要授予该用户 SELECT ANY DICTIONARY 系统权限。
你还可以使用 DBMS_SCHEDULER 包。通过SQL*Plus 调用DBMS_SCHEDULER使你完全可以通过命令行访问该调度程序。DBMS_SCHEDULER 还提供了将调度程序功能内嵌于用户应用程序中的能力。
创建你的第一个任务 要创建一个重复性任务,你首先需要定义程序和调度表。假定你需要在每隔一天的下午6:00加载一个外部表。首先创建一个调度程序,它提供一个 PL/SQL 块以执行该加载。代码清单1中的PL/SQL 块发出一个对DBMS_SCHEDULER.CREATE_PROGRAM的调用,作为程序操作所提供的 PL/SQL 块包含有一个简单的INSERT...SELECT FROM 语句,用于从该外部表中载入新的客户。
接着,创建一个调度表,根据该调度表运行该程序。代码清单2 给出一个对DBMS_SCHEDULER.CREATE_SCHEDULE的调用,用于创建我的 PERIODICLOADS调度表。注释和调度表名称参数相当简单。注释可以是提醒你为什么要创建该调度表的任意文本。schedule_ name 参数指明该调度表的所有者和名称。与程序和任务相类似,调度程序也是模式对象,它们的名称必须符合与表、索引等相同的命名规则。
start_date为一个TIMESTAMP WITH TIME ZONE 值,它指明该调度表什么时候被激活。此调度表在(美国东部时间)2004年3月22日星期一开始时被激活。
此调度程序引入了一个功能强大的日历语法,你可以用此语法调度重复性任务。此语法具有三个组件: 频度、间隔和时刻。你还有一个选项:基于一个PL/SQL表达式定义重复间隔(如果你对一个任务定义了一个内嵌调度程序),但是我相信你可能找到一种日历语法,它能满足除最不常用的调度表以外的所有调度表的需要。以下语句是我的PERIODICLOADS 调度表中repeat_interval参数中的日历语法:
FREQ=DAILY;INTERVAL=2;BYHOUR=18;
BYMINUTE=0;BYSECOND=0
FREQ=DAILY 根据PERIODICLOADS 调度表规定,任何任务相继运行之间的重复时间间隔以天或者24小时为单位计算。INTERVAL=2 指明单位的数目,在此例中为2天。三个 BY 数值-BYHOUR=18, BYMINUTE=0, and BYSECOND=0-指明任务运行的时刻,以24小时制表示。代码清单2中的PERIODICLOADS 调度表将给出以下任务运行时间:
第一次运行是在2004年3月22日星期一,下午6:00。
第二次运行是在2004年3月22日星期一的两天之后,即3月24日星期三的下午6:00。
第三次运行是在2004年3月22日星期一的两天之后再过两天,即3月26日星期五下午6:00。
该日历语法的一个非常好的特色就是它消除了使用DBMS_JOB时可能会发生的调度表时间偏移。一个任务实例的起始时间不会影响下一个任务实例的起始时间。相反,调度表的重复时间间隔总是根据调度表的start_date重新进行计算。
该日历算法的通用性很好。 你不仅可以为每个 BY 关键字指定一个单一取值,而且还可以指定一个取值列表。例如,如果你希望总在星期一、星期三和星期五进行周期性加载,那么你可以指定重复时间间隔如下:
FREQ=WEEKLY;INTERVAL=1;
BYDAY=MON,WED,FRI;BYHOUR=18;BYMINUTE=0;
BYSECOND=0
此调度表将每周重复一次 (INTERVAL=1);它将在每周的星期一、星期三和星期五启动任务 (BYDAY=MON,WED,FRI);并且这些任务将在这些天的每个下午6:00 p.m (BYHOUR=18; BYMINUTE=0;BYSECOND=0) 运行。
在你已经定义了程序和调度表之后,下一个步骤就是将这两个对象以一个任务的形式连接在一起。代码清单3显示一个对DBMS_SCHEDULER.CREATE_JOB的调用,它创建一个名为 LOADNEWCUSTOMERS的任务。此任务将根据PERIODICLOADS调度表运行LOADCUSTOMERS程序。
将调度表、程序和任务分离可以实际减少你的工作量。你可以在任何时间基于PERIODICLOADS调度表创建一个新的任务,而且此新任务将在与刚创建的LOADNEWCUSTOMERS任务相同的时刻和以相同的时间间隔运行。
代码清单2 中定义的调度表给出以下任务开始运行的时间:
2004年3月22日星期一,下午6:00。
2004年3月24日星期三,下午6:00。
2004年3月26日星期五,下午6:00。
当你将一个任务连接到此调度表时,该任务将在你将两者连接在一起的日期和时间之后的下一个调度时间运行。例如,如果你在2004年3月23日星期二创建一个如代码清单3 所示的任务,此任务将在3月24日星期三的下午6:00第一次运行。如果你拥有按照同一调度表运行的几个相关任务,并且你需要修改该调度表,那么对整个相关任务集只需修改一次。
限定任务的起始时间 这一新的调度程序的一个非常好的特性是能够将一个任务的启始时间限定在正常启动时间之后的一个特定时期。例如,假定你有一个安排在每天晚上6:00运行的任务。由于系统加载或其它原因,此任务实际上可能要等到6:12才能有开始运行的机会。即使是延迟了12分钟,你是否仍然希望开始运行该任务?
采用这一调度程序,你可以回答希望在延后多长时间后你仍然愿意开始运行一个任务的问题。你可以通过设置该任务的schedule_limit属性值(以分为单位)来完成这一工作。以下语句指明我的LOADNEWCUSTOMERS任务必须在其预定开始运行的时间之后的10分钟内开始运行:
sys.dbms_scheduler.set_attribute(
name =>'GENNICK.LOADNEWCUSTOMERS',
attribute => 'schedule_limit',
value => 10);
如果这一任务被延后到超过其开始运行的时间10分钟才能运行,则将取消(跳过)此任务的执行。 默认的 schedule_limit属性为空,则意味着没有任何限制。当使用数据库控制时,你可以利用"编辑任务"页面的"选项"选项卡设置这一属性。
创建资源窗口 作为一个DBA,你可以创建资源计划,以将资源分配给不同的使用者组。利用调度程序窗口,你可以使这些资源计划自动生效。代码清单4 给出了创建一个夜间批处理窗口的代码,这个窗口在每晚6:00开始运行,一直运行到第二天早晨8:00。请注意resource_plan 参数。该窗口激活BATCH_PROCESSING 资源计划。与此类似,你可以创建一个第二窗口,它在每天早晨激活ONLINE_PROCESSING资源计划。
任务类允许你将任务分派给使用者组,它是资源管理与调度程序之间的连接。默认的任务类为DEFAULT_JOB_CLASS,但是在创建一个任务时,你可以很容易地选择一个不同的类。你还可以在创建任务之后很容易地由一个任务切换至另一个任务。
类允许你将任务分派给使用者组,它是资源管理与调度程序之间的连接。默认的任务类为DEFAULT_JOB_CLASS,但是在创建一个任务时,你可以很容易地选择一个不同的类。你还可以在创建任务之后很容易地由一个任务切换至另一个任务。
代码清单4 中所创建的窗口其定义带有一个内嵌调度表,这就是说该调度表是该窗口定义的一部分。还有可能将一个窗口连接到一个单独定义的调度表。你可以更进一步将一个窗口和几个相关的任务连接到相一个调度表,这样,当该调度表被触发时,该窗口的资源计划将被激活,所有连接到该调度表的任务便开始运行。
如果你有一些任务,只希望它们在窗口及其资源计划被激活时运行,那么你可以根据窗口而不是根据预定的调度表来调度这些任务。如果你有两个或多个窗口共享同一个资源计划,则你可将这些窗口结合成一个窗口组。以下语句说明了如何创建一个包含夜间和周末运行的窗口的批处理窗口组:
BEGIN
DBMS_SCHEDULER.CREATE_WINDOW_GROUP(
group_name=>'batch_processing',
window_list=>'NIGHT,WEEKEND');
END;
监控与日志 这一新的调度程序提供了卓越的监控和日志工具,它们特别便于通过Oracle 企业管理器 10g的数据库控制界面来使用。图 3 给出了你可以从"调度程序任务"页看到的已排定的任务列表。点击"运行"选项卡可以查看当前运行的任务。点击"运行历史"选项卡,你会看到类似于图 4中的一个日志。 每个日志条目表示一个任务实例,你可以立即看到哪个实例运行成功了,哪个实例运行失败了。点击一个任务名称可以深入到一任务,你还可以进一步深入查看每次运行的详细日志条目。 日志条目将被保存你所指定的一段时间。你可以为日志条目指定一个全局保持时间,你也可以为不同的工作类定制日志条目的保持时间。调度程序将每天检查一次已经超过其保持时间的日志条目,并将它们删除。
图3: 调度程序的已排定任务选项卡
图4: 调度程序的任务运行历史选项卡 对于任何希望减少调度、管理和监控重复性任务的单调乏味程度的DBA来说,这一新的调度程序及其在数据库控制中的强力支持都是深受欢迎的技术成就。程序、调度表、任务、任务类、窗口和窗口组的体系结构是一个功能强大的特性集,并且为在未来版本中进一步提高打下了基础。相对于编写PL/SQL表达式来计算任务执行时间间隔的老DBMS_JOB方法,日历语法的使用更简单、更可靠。这一新的调度程序可以提供很多功能。请很好地利用它吧。