Oracle 中的作业队列和队列调度

一,启动执行作业的进程
 
    在 Oracle 中,是使用 “作业队列协调进程(CJQ0)” 这个协调数据库实例的作业队列的后台进程,来监视作业队列中的作业表(JOB$),并启动作业队列进程(Jnnn)。而当需要执行作业的时候,作业队列Jnnn 将会执行由 DBMS_JOBS 包创建的作业请求,如下面的一个过程就是从 DBMS_JOBS 包中提取出来的
    
    * 提交的作业请求的过程声明:
    
    PROCEDURE submit    ( job       OUT BINARY_INTEGER,
                          what      IN  VARCHAR2,
                          next_date IN  DATE DEFAULT sysdate,
                          interval  IN  VARCHAR2 DEFAULT ‘null‘,
                          no_parse  IN  BOOLEAN DEFAULT FALSE,
                   instance  IN  BINARY_INTEGER DEFAULT 0,
                   force     IN  BOOLEAN DEFAULT FALSE );
    -- Submit a new job.  Chooses JOB from the sequence sys.jobseq.
    -- instance and force are added for jobq queue affinity
    -- If FORCE is TRUE, then any positive  integer is acceptable as the job
    -- instance. If FORCE is FALSE, then the specified instance must be running;
    -- otherwise the routine raises an exception.
    -- For example,
    --   variable x number;
    --   execute dbms_job.submit(:x,‘pack.proc(‘‘arg1‘‘);‘,sysdate,‘sysdate+1‘);
    
    下面再通过包中给出的参数的含义就可以知道当我们需要添加作业的时候的各个参数各有什么含义了:
    - job       指定被执行的作业的作业号
    - what      指定需要被执行的 pl/sql 代码
    - next_date 作业下一次运行时间,缺省值为 sysdate
    - interval  这里是用来计算下一次作业执行时间的日期函数,缺省为 NULL。(基本是估算)
    - no_parse  一个标记。false(缺省): Oracle 将分析与作业相关联的过程。true: 将分析与首次执行的作业相关联的过程。例如:如果希望在创建与作业相关联的表完成之前提交作业,就需要设置为 true。
    
    * 删除先前提交的作业:
    
    PROCEDURE remove    ( job       IN  BINARY_INTEGER );
    -- Remove an existing job from the job queue.
    -- This currently does not stop a running job.
    --   execute dbms_job.remove(14144);
    
    * 更改作业:
    
    PROCEDURE change    ( job       IN  BINARY_INTEGER,
                          what      IN  VARCHAR2,
                          next_date IN  DATE,
                          interval  IN  VARCHAR2,
     instance  IN  BINARY_INTEGER DEFAULT NULL,
     force     IN  BOOLEAN DEFAULT FALSE);
    -- Change any of the the user-settable fields in a job
    -- Parameter instance and force are added for job queue affinity
    -- If what, next_date,or interval is null, leave that value as-is.
    -- instance defaults to NULL indicates instance affinity is not changed.
    -- If FORCE is FALSE, the specified instance (to which the instance number
    -- change) must be running. Otherwise the routine raises an exception.
    -- If FORCE is TRUE, any positive  integer is acceptable as the job instance.
    --   execute dbms_job.change( 14144, null, null, ‘sysdate+3‘);
    
    * 对指定的作业修改作业说明
    
    PROCEDURE what      ( job       IN  BINARY_INTEGER,
                          what      IN  VARCHAR2 );
    -- Change what an existing job does, and replace its environment
    
    * 修改指定作业的下一次执行时间
    
    PROCEDURE next_date ( job       IN  BINARY_INTEGER,
                          next_date IN  DATE     );
    -- Change when an existing job will next execute
    
    * 修改指定作业的执行间隔
    
    PROCEDURE interval  ( job       IN  BINARY_INTEGER,
                          interval  IN  VARCHAR2 );
    -- Change how often a job executes
 
    * 设置或者重新设置作业损坏标记,如果作业损坏,则作业不能运行
    
    PROCEDURE broken    ( job       IN  BINARY_INTEGER,
                          broken    IN  BOOLEAN,
                          next_date IN  DATE DEFAULT SYSDATE );
    --  Set the broken flag.  Broken jobs are never run.
    
    * 强制执行某个指定的作业
    
    PROCEDURE run       ( job       IN  BINARY_INTEGER,
                   force     IN  BOOLEAN DEFAULT FALSE);
    --  Run job JOB now.  Run it even if it is broken.
    --  Running the job will recompute next_date, see view user_jobs.
    --    execute dbms_job.run(14144);
    --  Warning: this will reinitialize the current session‘s packages
    --  FORCE is added for job queue affinity
    --  If FORCE is TRUE, instance affinity is irrelevant for running jobs in
    --  the foreground process. If FORCE is FALSE, the job can be run in the
    --  foreground only in the specified instance. dbms_job.run will raise an
    --  exception if FORCE is FALSE and the connected instance is the wrong one.
    
    
    其中,初始化参数 JOB_QUEUE_PROCESSES 控制数据库实例能否启动一个协调作业队列程序进程。如果这个参数值为 0,表示在数据库启动的时候不启动作业队列协调进程,当然也不会执行作业队列中的任何作业。该参数也可以指定在一个实例上能够并发运行 Jnnn 进程的最大数目。可以指定的最大进程数目为 1000。
    
    并且该参数是动态的,可以使用如下语句动态更改设置:
    
    alter system set JOB_QUEUE_PROCESSES = 20;
    
二,管理作业队列
 
    1,要使用以及管理作业队列中的作业,可以使用 DBMS_JOB 包中的过程,并且使用作业队列并不需要数据库权限,只要你有执行作业队列过程的权限既可。
    
    2,下面举些例子吧,这样更 “实惠”:)
    
        declare 
          -- Local variables here
          jobno    number;
        begin
          dbms_job.submit(jobno, ‘dbms_ddl.analyze_object(‘‘TABLE‘‘,‘‘SCOTT‘‘, ‘‘EMP‘‘, ‘‘ESTIMATE‘‘, ‘‘NULL‘‘, 50);‘, SYSDATE, ‘SYSDATE+1‘);
        end;    
        
    3,下面是和作业(JOB)相关的一些属性:
    
        * 作业所有者
        * 作业号码      队列中的作业用作业号码进行标识。当提交作业时,它的作业号码将从 sys.jobseq 序列中自动生成。一旦分配就不可以再更改作业号码了。
        * 作业定义      就是提交作业(submit)时指定的 what 需要执行的 pl/sql 代码。这里需要注意的是在作业定义中,需要使用两个单引号括住字符串哦,如同使用动态 sql 中的字符串;还有,不能从一个作业中运行另一个作业。
        * 作业执行间隔  用于指定两次作业执行的间隔时间,也可以认为你的作业向间隔多长时间执行一次,特别适合那些周期性的任务。
        
        一些常用的日期表达式:
        
            - sysdate+7         每周运行一次
            - sysdate+1/48      每半小时运行一次
            - next_day(trunc(sysdate), "MONDAY")+15/24  每个星期一的下午 3 点开始运行
            - next_day(add_months(trunc(sysdate, "Q"), 3), ‘TRUSDAY‘)   每个季度的第一个星期四
            注意:
            
            (1) 例如,如果在周一设置了执行间隔为“sysdate+7”, 但是因某种原因,没有执行,直到周四才执行,则下次执行的时间是在每周四开始执行作业了。如果 interval 日期函数求出来的值是 NULL,
            
            (2) 当然,如果你希望你作业一直在某个时间点开始执行,而不管上次执行的结束时间,则你的 interval 和 next_date 参数都应该如下方式指定:next_day(trunc(sysdate), "MONDAY")
            
    4,下面我们再看看作业队列是如何运行的,以及其运行状态的查看
    
        (1) Oracle 通过使用作业队列锁来确保每次作业只是在一个会话中运行。可以使用锁视图来查看会话程序当前锁的相关信息:
        
        select * from v$lock where type = ‘JQ‘;
        
        也可以通过查看正在运行的作业的情况:
        
        select * from dba_jobs_running;
        
        (2) 作业执行错误,有关失败的信息会纪录在跟踪文件和报警日志中,并且 Oracle 会写入编号为 Ora-12012 的消息并包括失败作业的作业号。
        
        (3) 删除作业,dbms_job.remove(作业号);
        
        (4) 更改作业,
        
            - dbms_job.change(作业号,NULL,NULL,‘SYSDATE+3‘);
                
                将作业改为每三天运行一次
            
            - dbms_job.what(作业号,‘scott.emppackage.give_raise(‘‘abc‘‘, 6000.00);‘);
            
                改变作业的定义
            
            - dbms_job.next_date(作业号,‘SYSDATE+5‘);
            
                改变作业的下一次执行时间
            
            - dbms_job.interval(作业号,NULL);
            
                成功执行作业之后将不再运行该作业
                
三,查看作业队列信息
 
    相关的数据字典视图有:
    
        * dba_jobs          数据库中的所有作业
        * all_jobs          当前用户可以访问的所有作业
        * user_jobs         属于当前用户的所有作业
        * dba_jobs_running  列出数据库中当前运行的所有作业

Oracle 中的作业队列和队列调度

上一篇:数据库 MySQL 数据导入导出


下一篇:Oracle知识梳理