您的位置:首页 > 数据库 > Oracle

Oracle中的定时执行任务job

2017-05-04 17:32 363 查看
众所周知,一般操作系统会提供定时执行任务的方法,例如:Unix平台上提供了 让系统定时执行任务的命令Crontab。但是,对于某些需求,例如:一些对数据库表的操作,最为典型的是证券交易所每日收盘后的结算,它涉及大量的数据
库表操作,如果仍然利用操作系统去定时执行,不仅需要大量的编程工作,而且还会出现用户不一致等运行错误,甚至导致程序无法执行。 

一、分析问题 

事实上,对于以上需求,我们可以利用数据库本身拥有的功能Job Queue(任务队列管理器)去实现。任务队列管理器允许用户提前调度和安排某一任务,使其能在指定的时间点或时间段内自动执行一次或多次,由于任务在数据库中被执行,所以执行效率很高。 

任务队列管理器允许我们定制任务的执行时间,并提供了灵活的处理方式,还可以通过配置,安排任务在系统用户访问量少的时段内执行,极大地提高了工作效率。 例如,对于数据库日常的备份、更新、删除和复制等耗时长、重复性强的工作,我们就可以利用任务队列管理器去自动执行以减少工作量。 

目前,拥有此项功能的数据库有许多,最有代表性的是SQL Server 7.0、Oracle 8.0.5等。但是,要让任务队列管理器工作,还需要我们加以配置才能实现。SQL
Server的功能配置是在一个图形化界面(GUI)中实现的,非常简单。利用OEM客户端管理工具,Oracle的配置也可以在一个图形界面中完成。然 而大多数的用户更习惯于命令行的方式去操纵数据库。本文介绍如何通过命令行实现这种配置。 

二、实现步骤 

1.确保Oracle的工作模式允许启动任务队列管理器 

Oracle定时执行“Job Queue”的后台程序是SNP进程,而要启动SNP进程,首先要确保整个系统的模式是可以启动SNP进程的,这需要以DBA的身份去执行如下命令: 

svrmgrl> alter system enable restricted session; 

或sql> alter system disenable restricted session; 

利用如上命令更改系统的会话方式为disenable restricted,为SNP的启动创造条件。 

2.确保Oracle的系统已经配置了任务队列管理器的启动参数 

SNP的启动参数位于Oracle的初始化文件中,该文件放在$ORACLE_HOME/dbs路径下,如果Oracle的SID是myora8的话,则初始化文件就是initmyora8.ora,在文件中对SNP启动参数的描述部分如下: 

job_queue_process=n 

job_queue_interval=N 

第一行定义SNP进程的启动个数为n。系统缺省值为0,正常定义范围为0~36,根据任务的多少,可以配置不同的数值。 

第二行定义系统每隔N秒唤醒该进程一次。系统缺省值为60秒,正常范围为1~3600秒。事实上,该进程执行完当前任务后,就进入睡眠状态,睡眠一段时间后,由系统的总控负责将其唤醒。 

如果该文件中没有上面两行,请按照如上配置添加。配置完成后,需要重新启动数据库,使其生效。注意:如果任务要求执行的间隔很短的话,N的配置也要相应地小一点。 

3.将任务加入到数据库的任务队列中 

调用Oracle的dbms_job包中的存储过程,将任务加入到任务队列中: 

dbms_job.submit( job out binary_integer, 

what       in   archar2, 

next_date     in   date, 

interval     in   varchar2, 

no_parse     in   boolean) 

其中: 

●job:输出变量,是此任务在任务队列中的编号; 

●what:执行的任务的名称及其输入参数; 

●next_date:任务执行的时间; 

●interval:任务执行的时间间隔。 

下面详细讨论一下dbms_job.submit中的参数interval。严格地讲,interval是指上一次执行结束到下一次开始执行的时间间隔, 当interval设置为null时,该job执行结束后,就被从队列中删除。假如我们需要该job周期性地执行,则要用‘sysdate+m’表示。

============================================================

任务重复运行的时间间隔取决于interval参数中设置的日期表达式。下面就来详细谈谈该如何设置interval参数才能准确满足我们的任务需求。一般来讲,对于一个任务的定时执行,有三种定时要求。

在一个特定的时间间隔后,重复运行该任务。 

在特定的日期和时间运行任务。 

任务成功完成后,下一次执行应该在一个特定的时间间隔之后。 

第一种调度任务需求的日期算法比较简单,即'SYSDATE+n',这里n是一个以天为单位的时间间隔。表1给出了一些这种时间间隔设置的例子。

表1 一些简单的interval参数设置例子

描述 Interval参数值 

每天运行一次 'SYSDATE + 1' 

每小时运行一次 'SYSDATE + 1/24' 

每10分钟运行一次 'SYSDATE + 10/(60*24)' 

每30秒运行一次 'SYSDATE + 30/(60*24*60)' 

每隔一星期运行一次 'SYSDATE + 7' 

不再运行该任务并删除它 NULL

表1 所示的任务间隔表达式不能保证任务的下一次运行时间在一个特定的日期或者时间,仅仅能够指定一个任务两次运行之间的时间间隔。例如,如果一个任务第一次运 行是在凌晨12点,interval指定为'SYSDATE + 1',则该任务将被计划在第二天的凌晨12点执行。但是,如果某用户在下午4点手工(DBMS_JOB.RUN)执行了该任务,那么该任务将被重新定时到 第二天的下午4点。还有一个可能的原因是如果数据库关闭或者说任务队列非常的忙以至于任务不能在计划的那个时间点准时执行。在这种情况下,任务将试图尽快 运行,也就是说只要数据库一打开或者是任务队列不忙就开始执行,但是这时,运行时间已经从原来的提交时间漂移到了后来真正的运行时间。这种下一次运行时间
的不断“漂移”是采用简单时间间隔表达式的典型特征。

第二种调度任务需求相对于第一种就需要更复杂的时间间隔(interval)表达式,表7是一些要求在特定的时间运行任务的interval设置例子。

表 2. 定时到特定日期或时间的任务例子

描述 INTERVAL参数值 

每天午夜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)' 

每个季度最后一天的晚上11点 'TRUNC(ADD_MONTHS(SYSDATE + 2/24, 3 ), 'Q' ) -1/24' 

每星期六和日早上6点10分 'TRUNC(LEAST(NEXT_DAY(SYSDATE, ''SATURDAY"), NEXT_DAY(SYSDATE, "SUNDAY"))) + (6×60+10)/(24×60)'

第三种调度任务需求无论通过怎样设置interval日期表达式也不能满足要求。这时因为一个任务的下一次运行时间在任务开始时才计算,而在此时是不知道 任务在何时结束的。遇到这种情况怎么办呢?当然办法肯定是有的,我们可以通过为任务队列写过程的办法来实现。这里我只是简单介绍以下,可以在前一个任务队 列执行的过程中,取得任务完成的系统时间,然后加上指定的时间间隔,拿这个时间来控制下一个要执行的任务。这里有一个前提条件,就是目前运行的任务本身必 须要严格遵守自己的时间计划。

将任务加入到任务队列之前,要确定执行任务的数据库用户,若用户是scott, 则需要确保该用户拥有执行包dbms_job的权限;若没有,需要以DBA的身份将权利授予scott用户: 

svrmgrl> grant execute on dbms_job to scott; 

4.将要执行的任务写成存储过程或其他的数据库可执行的pl/sql程序段 

例如,我们已经建立了一个存储过程,其名称为my_job,在sql/plus中以scott用户身份登录,执行如下命令: 

sql> variable n number; 

sql> begin 

dbms_job.submit(:n‘my_job;’,sysdate, 

‘sysdate+1/360’); 

commit; 

end; 



系统提示执行成功。 

Sql> print :n; 

系统打印此任务的编号,例如结果为300。 

如上,我们创建了一个每隔4分钟执行一次的任务号为300的任务。可以通过Oracle提供的数据字典user_jobs察看该任务的执行情况: 

sql> select job,next_date,next_sec,failures,broken from user_jobs; 

执行结果如下: 

job next_date next_sec failures broken 

300 2000/10/10 11:45:15 0 N 

这表示任务号为300的任务,下一次将在2000/10/10 11:45:15执行,此任务的执行失败记录为0次。注意:当执行job出现错误时,Oracle将其记录在日志里,失败次数每次自动加1。当执行失败次 数达到16时,Oracle就将该job标志为broken。此后,Oracle不再继续执行它,直到用户调用过

--------------------------------------------------------------------------------------------------------------------

通过前台界面建立的job有时候不能成功 因为她生成的what=>存储过程中少了一个分号.

DECLARE

X NUMBER;

BEGIN

SYS.DBMS_JOB.SUBMIT

( job       => X 

   ,what      => 'test_job;'

   ,next_date => to_date('23/12/2008 00:00:00','dd/mm/yyyy hh24:mi:ss')

   ,interval => 'TRUNC(SYSDATE+1)'

   ,no_parse => FALSE

);

SYS.DBMS_OUTPUT.PUT_LINE('Job Number is: ' || to_char(x));

COMMIT;

end ;

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: