ITPub博客

首页 > 数据库 > Oracle > ORACLE 10G 新特性之------调度程序(dbms_job )

ORACLE 10G 新特性之------调度程序(dbms_job )

原创 Oracle 作者:bulkaunt 时间:2007-01-17 17:08:18 0 删除 编辑

您是否厌倦了在 dbms_job 中手工管理时间间隔的繁琐,需要数据库内部有个新调度系统? 让我们就着眼于数据库本身来解决这个问题。

[@more@]

你们中的一部分人可能广泛使用 dbms_job 程序包来将数据库作业提交到后台运行,控制运行的时间或时间间隔,报告故障等等。 然而,我感觉你们中的大部分人不会这么做。

这个程序包存在的问题是它只能够处理 PL/SQL 代码段 — 仅能处理匿名程序块和存储程序单元。 它不能在数据库外部处理操作系统命令文件或可执行文件中的任何东西。 为此,您将不得不求助于操作系统调度实用工具(如 Unix 中的 cron 或 Windows 中的 AT 命令)。 或者,您可以使用甚至可能通过提供图形用户界面来扩展这种功能的第三方工具。

虽然如此,dbms_job 有一个超过这些替代方法的独特优点: 它只有在数据库启动并运行时才有效。 如果数据库关闭,则作业不会运行。 数据库外部的工具必须人工检查数据库是否启动 — 而这可能很困难。 另一个优点是 dbms_job 在数据库内部,因此您可以通过数据库访问实用工具(如 SQL*Plus)来访问它。

Oracle 数据库 10g 调度程序特性提供了各方面的好处: 它是直接在数据库内部的一个作业调度实用程序,强大到足够处理所有类型的作业,而不只是 PL/SQL 代码段。 最好的一点是,它是数据库自带的,无需任何额外的成本。 在这一部分中,我们将看看它是如何工作的。

将作业与程序连接

也许最好通过一个例子来介绍这个概念。 假定您创建了一个 shell 脚本,以将存档的日志文件转移到一个不同的文件系统中,如下:

/home/arup/dbtools/move_arcs.sh
首先,您需要使数据库知道这个脚本是一个要在作业中使用的程序。 要创建这个程序,您必须拥有 CREATE JOB 权限。
begin
    dbms_scheduler.create_program
    (
       program_name   => 'MOVE_ARCS',
       program_type   => 'EXECUTABLE',
       program_action => '/home/arup/dbtools/move_arcs.sh',
       enabled        => TRUE,
       comments       => 'Moving Archived Logs to Staging Directory'
    );
end;
/
这里您创建了一个命名程序单元,将其指定为可执行文件。注意这个程序单元叫什么。

接下来,您将创建一个每 30 分钟运行一次的命名计划,该计划的名称为 EVERY_30_MINS。 您将使用以下命令来完成这一操作:

begin
    dbms_scheduler.create_schedule
    (
       schedule_name   => 'EVERY_30_MINS',
       repeat_interval => 'FREQ=MINUTELY; INTERVAL=30',
       comments        => 'Every 30-mins'
    );
end;
/

现在创建了程序和计划,接着您将把程序与计划连接来创建作业。

begin
   dbms_scheduler.create_job
   (
      job_name      => 'ARC_MOVE',
      program_name  => 'MOVE_ARCS',
      schedule_name => 'EVERY_30_MINS',
      comments      => 'Move Archived Logs to a Different Directory',
      enabled       => TRUE
   );
end;
/
这将创建一个每 30 分钟运行一次的作业,该作业执行 shell 脚本 move_arcs.sh。它将由数据库内部的调度程序特性来处理 — 无需 cronAT 实用工具。

创建没有程序的作业

在上述情况下,您创建了一个程序来引用一个操作系统实用工具或程序,并创建了一个计划来指定运行的次数,最后将这两者连接起来创建作业。 不过,不需要从程序中创建作业;也可以独立地定义它们。 例如,您可以创建上述作业,而无需首先创建一个程序。

begin
   dbms_scheduler.create_job
   (
      job_name      => 'ARC_MOVE_2',
      schedule_name => 'EVERY_30_MINS',
      job_type      => 'EXECUTABLE',
      job_action    => '/home/arup/dbtools/move_arcs.sh',
      enabled       => true,
      comments      => 'Move Archived Logs to a Different Directory'
   );
end;
/
这里直接指定 OS 可执行文件,而无需首先将其创建为一个程序。 同样,您可以创建一个作业,而无需一个命名的计划。
begin
   dbms_scheduler.create_job
   (
      job_name        => 'ARC_MOVE_3',
      job_type        => 'EXECUTABLE',
      job_action      => '/home/arup/dbtools/move_arcs.sh',
      repeat_interval => 'FREQ=MINUTELY; INTERVAL=30',
      enabled         => true,
      comments        => 'Move Archived Logs to a Different Directory'
   );
end;
/
Scheduler 超过 dbms_job 的一个优点从我们最初的例子中可以很清楚地看出来: 能够调用 OS 实用工具和程序,而不只是 PL/SQL 程序单元。 这一能力使它成为管理 Oracle 数据库和相关作业的最全面的作业管理工具。

不过,您可能已经注意到了另一个同样重要的优点: 能够以自然语言定义时间间隔。 注意在上面的例子中,我们要我们的计划每 30 分钟运行一次,因此通过一个简单的类似英语的表达式(而不是 PL/SQL)定义了 REPEAT_INTERVAL 参数

'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
)
这种代码容易理解吗?实际上不容易。

现在让我们看看 DBMS_SCHEDULER 中的等价的作业。 REPEAT_INTERVAL 参数将像下面这么简单:

'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 过程来预览接下来的日期的计算。 利用第一个例子 — 从星期一到星期五每天在上午 7:00 和下午 3:00 运行 Statspack — 您可以按如下方式检查您的时间间隔字符串的准确性:

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
      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: 03/22/2004 07:00:00
Next Run on: 03/22/2004 15:00:00
Next Run on: 03/23/2004 07:00:00
Next Run on: 03/23/2004 15:00:00
Next Run on: 03/24/2004 07:00:00
Next Run on: 03/24/2004 15:00:00
Next Run on: 03/25/2004 07:00:00
Next Run on: 03/25/2004 15:00:00
Next Run on: 03/26/2004 07:00:00
Next Run on: 03/26/2004 15:00:00
这确认您的设置是正确的。

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/58242/viewspace-891093/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2012-09-21

  • 博文量
    15
  • 访问量
    1139889