日常的运维工作中,我们经常使用Linux Server的anacron服务来使得服务器执行一下计划之内的任务,可以按照特定的时间间隔,重复的执行相关的命令或者相关的脚本,来完成预期的目标,能够节省相关的人力,使得运维变得更加容易。
对于Oracle数据库来说,当然也提供了相关的功能来实现定时的,重复的完成PL/SQL Block,Shell Scripts(其实是External Executables,在这里简单用shell脚本代替),以及Oracle本身已经编写好的Storage Procedure或者用户自定义Function。
在早期的Oracle Database中,Job就是实现了该功能的主力军,通过DBMS_JOB包就可以完成对Job的定义,修改,删除。从Oracle 10g的版本开始,Oracle提供了更强大的功能--Scheduler Job,随着功能的强大,那么定义的方式也会变的相对更加的复杂,下面我们就开始对相关的知识进行一下探讨:
一、Scheduler Job Concepts
这里对Scheduler Job暂且简称为Job,如果后面有对之前版本使用的Job的对比,我会把其称之为“旧版的Job”。
对于Job的概念,为了严谨,我会从官方手册上进行摘录和翻译。
详情请参照官方手册“Administrator’s Guide”文档,28章节“Oracle scheduler Concepts”。
The Scheduler provides sophisticated, flexible enterprise scheduling functionality, which you can use to:
Run database program units
1.You can run program units, that is, PL/SQL anonymous blocks, PL/SQL stored procedures, and Java stored procedures on the local database or on one or more remote Oracle databases.
2.Run external executables, (executables that are external to the database)
You can run external executables, such as applications, shell scripts, and batch files, on the local system or on one or more remote systems. Remote systems do not require an Oracle Database installation; they require only a Scheduler agent. Scheduler agents are available for all platforms supported by Oracle Database and some additional platforms.
这里详细说明了,Oracle的Scheduler不仅仅可以提供自动运行本地Oracle程序单元(包括PL/SQL匿名块,PL/SQL存储过程,JAVA存储过程),还可以运行Oracle数据库之外的本地或者远程服务器上的可以执行程序,例如应用,shell脚本或者批处理文件。甚至在远程服务器上不需要安装Oracle数据库软件。远程主机仅仅需要Oracle Scheduler的代理软件,Oracle Scheduler代理软件能部署在任何支持Oracle软件安装的平台上,还有一些附加的平台之上。
对于Scheduler的定义方式,Oracle提供了以下三种方式:
1.基于时间(Time-based scheduling)的日程
这种方式就是我们最常见到的,功能类似于anacron的定义方式,它可以通过指定一段时间,甚至永久的日程,来重复的在指定的时间点完成某项特定的任务。
2.基于事件(Events-based scheduling)的日程
你可以在发生系统事件或者业务事件的时候,对它们进行回应来触发相关的计划任务,在我看来,就像触发器,满足特定条件,或者发生指定事件,就可以触发相关的任务。这也是通过anacron没有办法完成的一种强大功能。
3.独立(Dependency scheduling)日程
这一类的日程是可以通过上一件日程的完成来触发的,上一次的Job完成之后,则会触发下一个Job,这样就形成了一条“作业链”(Job Chain),需要和基于事件的日程区分的是,系统事件和业务事件并不是一丁是之前的Job。
二、Scheduler Data Dictionary Views
既然需要Job来替我们完成计划任务,我们就需要对其进行监控以防止中断的进程,无效的进程以及一些始料未及的情况,下面就来介绍一下相关的数据字典表。
View Description
--------------------------------------------------
*_SCHEDULER_CHAIN_RULES
These views show all rules for all chains.
*_SCHEDULER_CHAIN_STEPS
These views show all steps for all chains.
*_SCHEDULER_CHAINS
These views show all chains.
*_SCHEDULER_CREDENTIALS
These views show all credentials.
*_SCHEDULER_DB_DESTS
These views show all database destinations.
*_SCHEDULER_DESTS
These views show all destinations, both database and external.
*_SCHEDULER_EXTERNAL_DESTS
These views show all external destinations.
*_SCHEDULER_FILE_WATCHERS
These views show all file watchers.
*_SCHEDULER_GLOBAL_ATTRIBUTE
These views show the current values of Scheduler attributes.
*_SCHEDULER_GROUP_MEMBERS
These views show all group members in all groups.
*_SCHEDULER_GROUPS
These views show all groups.
*_SCHEDULER_JOB_ARGS
These views show all set argument values for all jobs.
*_SCHEDULER_JOB_CLASSES
These views show all job classes.
*_SCHEDULER_JOB_DESTS
These views show the state of both local jobs and jobs at remote destinations, including child jobs of multiple-destination jobs. You obtain job destination IDs (job_dest_id) from these views.
*_SCHEDULER_JOB_LOG
These views show job runs and state changes, depending on the logging level set.
*_SCHEDULER_JOB_ROLES
These views show all jobs by Oracle Data Guard database role.
*_SCHEDULER_JOB_RUN_DETAILS
These views show all completed (failed or successful) job runs.
*_SCHEDULER_JOBS
These views show all jobs, enabled as well as disabled.
*_SCHEDULER_NOTIFICATIONS
These views show all job state e-mail notifications.
*_SCHEDULER_PROGRAM_ARGS
These views show all arguments defined for all programs as well as the default values if they exist.
*_SCHEDULER_PROGRAMS
These views show all programs.
*_SCHEDULER_RUNNING_CHAINS
These views show all chains that are running.
*_SCHEDULER_RUNNING_JOBS
These views show state information on all jobs that are currently being run.
*_SCHEDULER_SCHEDULES
These views show all schedules.
*_SCHEDULER_WINDOW_DETAILS
These views show all completed window runs.
*_SCHEDULER_WINDOW_GROUPS
These views show all window groups.
*_SCHEDULER_WINDOW_LOG
These views show all state changes made to windows.
*_SCHEDULER_WINDOWS
These views show all windows.
*_SCHEDULER_WINGROUP_MEMBERS
These views show the members of all window groups, one row for each group member.
--------------------------------------------------
在这里我们经常使用的视图有
*_SCHEDULER_RUNNING_JOBS:描述正在运行的Job的相关信息
*_SCHEDULER_JOB_RUN_DETAILS:描述已经完成的Job是否成功,记录相关日志的视图,这里记录着所有已经执行完毕的日程
*_SCHEDULER_SCHEDULES:描述当前数据库所有已知的日程。
*_SCHEDULER_PROGRAMS:描述当前数据库已经定义完毕的程序。
*_SCHEDULER_JOBS:描述当前数据库已经定义完毕的作业。
对于日程,程序,作业的概念会在稍后介绍,请耐心往下看。
三、Schedule、Program、Job Concepts
对于它们的概念,可以从文字的字面意思上进行理解。
首先从Job这个最核心的部分开始理解吧,Job意为作业,也就是计划任务的本质,之前说过之前版本的Oracle Database也是通过Job来定义计划任务的,只不过这里的Job和旧版本的Job不太一样,他支持更多的功能。
Job作为整个Scheduler Job的核心,他是用来描述如何完成一个作业的。
它将会定义如下内容:
1.When
何时完成该项作业,指定时间,这也是计划任务中“计划”的含义,指定确定的时间完成确定的操作或者事件。当然,该项也可以不定义,通过在Schedule(日程)中指定时间和时间间隔更加合理,之后会有相应的解释。
2.Where(Destination)
究竟Job在哪里完成,之前在Scheduler Concepts我就介绍过,Job也可以在远程的主机上完成,当然需要进行Agent的部署,如果不对该项进行定义,那么该任务默认在本地完成。
3.How
这个很重要,因为你总要知道你在特定的时间点需要做什么事情吧?在之前的Scheduler Concepts我也提到,Scheduler Job可以定义多种任务实现方式。
1)PL/SQL anonymous Block
2)Shell Scripts
3)External Executables
我们可以在Job中定义这些Action(动作),当然也可以在Program中定义,我们稍后会讲解program。
现在我们来介绍一下schedule,日程,也就是用来定义今天或者截下来的几天,甚至今后的时间需要在某个特定的时间完成什么样的事情。
它仅仅用来定义时间和时间间隔,这样的说法比较抽象,下面用一个例子来说明:
我现在对Oracle非常感兴趣,想要对Oracle技术进行学习,那么我每天看Oracle的时间为固定1小时,不会被其他事情打扰,且必须要在17:00-20:00之间抽出一个小时,因为还有别的安排。那么我就会抽出最合理的一小时进行定义。
例如,我要在每周一到周五的19:00-20:00完成Oracle的学习,这是学习本身的时间,不能由我们定义,所以这里19-20点既不是指的时间也不是指的时间间隔,而是完成作业本身所需要的固定时间。
而19:00则是我所说的时间,也就是作业开始的时间,也就是说我会定义19点为作业的start_time。
那时间间隔是什么呢?我之前说了那就是每个周一到周五中的每一天,到了周五完成Job之后又需要72小时的等待才能进行下一次的任务。
那这里的每周一到周五的17点,就是我的Scheduler设置的时间间隔和开始时间。
那如何创建一个schedule呢?使用DBMS_SCHEDULER包就可以完成了。创建好的Job如果没有定义时间,那么就可以指定一个schedule,使用它定义的时间来完成Job。简单的来说,就是Job调用了Schedule来获取它要进行Job的时间并且参照该时间执行。
介绍完了schedule,那么program大家应该也就可以联想到了,也就是定义做什么事情,当Job没有定义做Action的时候则可以调用program中定义的action进行Job。
好了,说了那么多,究竟Oracle为什么要在新版本的Job中添加这么多的定义?最简单的例子,如果我想在同一时间做不同的很多事情,我就需要定义很多Job,那么这个Job需要定义很多次重复的时间。
如果我定义了schedule,我是否只需要将Job1~Jobn都调用同一个Schedule就好了呢?这样做事情的时间就统一了。
对于program就不用多说了吧?再简单不过的就是在不同的时间做同一间事情了,难不成你要把几百行的shell脚本定义好多遍?敲死你你也写不完。
四、Example
那么我现在想要在每天的13点对数据库进行一个全库的备份,下面就来演示一下如何去做。
首先我们需要去定义一下做什么,我们有一个备份脚本,叫做full_backup01.sh,它位于/home/oracle下。我们来查看一下它的内容。
$ cat /home/oracle/full_backup01.sh
#!/bin/bash
export ORACLE_SID=ora11g
export ORACLE_HOME=/u01/app/oracle/product/11.2.0/dbhome_1
#由于Linux执行脚本的时候会开启新的shell,不继承parent shell的环境变量,所以需要定义
export DATE=`date +%Y-%m-%d_%H:%M:%S`
export LOG=full_backup_$DATE.log
export DIR=/home/oracle/backup_log
if [ ! -d “$DIR” ]; then
mkdir -p $DIR
echo ‘Create Directory $DIR successful …’ > $DIR/$LOG
else
$ORACLE_HOME/bin/rman target / << EOF
run {
spool to /home/oracle/configuration.log;
show all;
spool off;
configure retention policy to redundancy 1;
configure backup optimization on;
configure controlfile autobackup on;
spool to “$DIR/$LOG”
allocate channel c1 type disk;
allocate channel c2 type disk;
backup incremental level 0 database plus archivelog;
release channel c1;
release channel c2;
spool off;
exit;
}
EOF
fi
这是一个非常简单的备份脚本,进行了一次0级增量备份,并且备份了archivelog,该脚本应该能够被Oracle用户执行。
$ chown oracle:oinstall /home/oracle/full_backup_01.sh
$ chmod u+x /home/oracle/full_backup_01.sh
首先我们来定义一个program,这个program描述了我们做什么,也就是执行该shell脚本
SQL> BEGIN
2 DBMS_SCHEDULER.CREATE_PROGRAM(
3 program_name => ‘my_program’,
4 program_type => ‘executable’,
5 program_action => ‘/home/oracle/full_backup_01.sh’,
6 enabled => true);
7 END;
8 /
PL/SQL procedure successfully completed.
这里有多个参数可以定义,但是最主要的是以上四个,有了它们,这个program就可以正常运行,首先是program的名字,既然作为数据库对象,就需要一个名字,这里不再赘述。
program type。这里有三种选项:
1.PLSQL_BlOCK
2.EXECUTABLE
3.STORED_PROCEDURE
也就对应着我之前写的三种情况,大家如果忘记了,请自行翻阅前面的内容。
program_type则对应着如何执行,对于shell脚本,执行绝对路径即可,对于其他两种,参照PLSQL语法进行写入即可。
enabled,是否开启该program,只有enabled的program才可以被Job调用,如果设置的false,想要手动启用的时候需要使用过程dbms_scheduler.enable()。
SQL> exec DBMS_SCHEDULER.ENABLE(‘MY_PROGRAM’)
PL/SQL procedure successfully completed.
这一步对于Job也是一样的,不再赘述。
好,完成了“做什么”,下面我们来定义一下何时去做。
也就是说我们现在要定义一个schedule,对于schedule的定义相对难一些,因为要考虑如何写时间间隔。
我们下面来定义一下,每天的下午3点执行备份脚本:
SQL> BEGIN
2 DBMS_SCHEDULER.CREATE_SCHEDULE(
3 schedule_name => ‘my_schedule’,
4 start_date => systimestamp,
5 end_date => systimestamp + interval ’30’ day,
6 repeat_interval => ‘FREQ=DAILY,BYHOUR=15’,
7 comments => ‘my test backup schedule’);
8 END;
9 /
PL/SQL procedure successful completed.
这里就有一些东西要交代了,每每涉及时间的时候,大家可能会很头疼,因为不清楚,这里似乎有三个参数是关于时间的,究竟该如何设置呢。
首先来讲一下start_date参数把。
start_date
This attribute specifies the first date and time on which this schedule becomes valid. For a repeating schedule, the value for start_date is a reference date. In this case, the start of the schedule is not the start_date; it depends on the repeat interval specified. start_date is used to determine the first instance of the schedule.
If start_date is specified in the past and no value for repeat_interval is specified, the schedule is invalid. For a repeating job or window, start_date can be derived from the repeat_interval if it is not specified.
If start_date is null, then the date that the job or window is enabled is used. start_date and repeat_interval cannot both be null.
官方文档对这个参数进行了合理的描述,这个参数指定了这个日程开始有效的时间和日期,对于一个可重复的日程,start_date是一个参考日期。
在这个例子当中,这个schedule的开始日期取决于repeat_interval的值,而不是start_date的值。
如果start_date在之前指定了,但是repeat interval没有指定,那么这个计划是无效的,对于一个重复执行的job,start_date如果没有被设置则可以由repeat interval来决定。
如果start_date是null值,那么Job开始的时间就是Job被enable的时间,所以说,start_date和repeat_interval不能是null值。
也就是说这个Job将会在指定时间以后的第一次符合repeat interval时间间隔的时间运作。
所以如果创建的语句写成以下的形式:
SQL> BEGIN
2 DBMS_SCHEDULER.CREATE_SCHEDULE(
3 schedule_name => ‘my_schedule’,
4 repeat_interval => ‘FREQ=DAILY,BYHOUR=15’,
5 comments => ‘my test backup schedule’);
6 END;
7 /
PL/SQL procedure successfully completed.
则Job在调用该schedule的时候立马生效,而且永远都将不过期,而何时过期是由end_date来决定的,所以第一个例子的计划任务将会在30天以内有效。
关于repeat_interval参数就要好好的解释一下了:
这个参数有很多的字句,这里我去官方文档进行摘取子句的内容,大家大概了解一下其中的写法,有一个初步的了解。
The following examples illustrate simple repeat intervals. For simplicity, it is assumed that there is no contribution to the evaluation results by the start date.
Run every Friday. (All three examples are equivalent.)
FREQ=DAILY; BYDAY=FRI; 每个周五都运行一遍(下面两条一样)
FREQ=WEEKLY; BYDAY=FRI;
FREQ=YEARLY; BYDAY=FRI;
Run every other Friday.
FREQ=WEEKLY; INTERVAL=2; BYDAY=FRI;(每两个周五运行一次)
Run on the last day of every month.
FREQ=MONTHLY; BYMONTHDAY=-1;(每个月的上一天搞一下!)
Run on the next to last day of every month.
FREQ=MONTHLY; BYMONTHDAY=-2;(每个月的前两天搞一下!)
Run on March 10th. (Both examples are equivalent)
FREQ=YEARLY; BYMONTH=MAR; BYMONTHDAY=10;(每年的三月十号运行)
FREQ=YEARLY; BYDATE=0310;
Run every 10 days.
FREQ=DAILY; INTERVAL=10;
Run daily at 4, 5, and 6PM.
FREQ=DAILY; BYHOUR=16,17,18;
Run on the 15th day of every other month.
FREQ=MONTHLY; INTERVAL=2; BYMONTHDAY=15;
Run on the 29th day of every month.
FREQ=MONTHLY; BYMONTHDAY=29;
Run on the second Wednesday of each month.
FREQ=MONTHLY; BYDAY=2WED;
Run on the last Friday of the year.
FREQ=YEARLY; BYDAY=-1FRI;
Run every 50 hours.
FREQ=HOURLY; INTERVAL=50;
Run on the last day of every other month.
FREQ=MONTHLY; INTERVAL=2; BYMONTHDAY=-1;
Run hourly for the first three days of every month.
FREQ=HOURLY; BYMONTHDAY=1,2,3;
官方文档给了好多好多例子,这些例子确实看起来都有点厉害!真的有那么难写么?其实不是,FREQ代表的是频率,是每年,还是每个月,还是每天。
BYDAY,BYMONTH,BYMONTHDAY就代表着,哪一天,哪一个月,这个月的第几天。
而INTERVAL=n,就代表着每几个这样的间隔就执行一次。举个例子:
repeat_interval => ‘FREQ=DAILY;BYHOUR=15,16;INTERVAL=2’
那么频率就是每天,interval=2,就代表每两天,每两天的15点和16点运行一下Job。如果前面是MONTHLY,那么就是每两个月了。
不难理解吧?如果还有其他写法,在官方文档上都会有介绍。YEARLY这种表示时间间隔的词我就不在这里进行赘述了,大家可以参照官方文档的PL/SQL Packages and Types References,里面的DBMS_SCHEDULER文档阅读一下。
最后的核心内容则是创建一个Job了,如果运行的时间和运行的程序都在Job里进行定义,那么参数和之前的program还有schedule所定义的一样。下面我来创建一个调用之前创建的Schedule和Program的一个Job。
SQL> BEGIN
2 DBMS_SCHEDULER.CREATE_JOB(
3 job_name => ‘my_job’,
4 program_name => ‘my_program’,
5 schedule_name => ‘my_schedule’,
6 enabled => true);
7 END;
8 /
PL/SQL procedure successfully completed.
这里有个参数叫做destination_name和auto_drop,我并没有写入,第一个参数代表可以在远程数据库上或者远程主机执行,这个需要部署agent,之前提到过。
而auto_drop则代表,在这个Job在超过了自己定义的或者schedule里定义的end_date之后,会自动被drop掉,而不是被置为disable状态。
到此为止,我们创建和配置一个完整的Job就已经结束了。如果要对这些Job进行监控,很简单,如下视图和sql可以做到。
SQL> select schedule_name,start_date,repeat_interval,end_date
2 from dba_scheduler_schedules
3 where lower(schedule_name) = ‘my_schedule’ ;
同理查询dba_scheduler_programs和dba_scheduler_jobs来查看program和job的相关信息。
如果想要看Job的执行后状态,可以使用以下SQL。
SQL> select log_id,log_date,status,additional_info
2 from user_scheduler_job_run_details
3 where lower(job_name)=‘my_job’;
转:http://blog.itpub.net/31401355/viewspace-2129311/