【问题标题】:Stop Scheduler Job - Exit Point in PL/SQL Block停止调度程序作业 - PL/SQL 块中的退出点
【发布时间】:2017-10-11 18:07:35
【问题描述】:
DBMS_SCHEDULER.CREATE_JOB (
    job_name => 'MYJOB', 
    job_type => 'PLSQL_BLOCK',
    job_action => <see below>
);

DBMS_SCHEDULER.ENABLE (
    name => 'MYJOB'
);

-- pl/SQL block for the job:

BEGIN
    -- 1
    UPDATE ...
    COMMIT;

    FOR x IN (SELECT ...
    LOOP
        -- 2
        UPDATE ...
        COMMIT;
    END LOOP;

    -- 3
    UPDATE ...
    COMMIT;
END

当我执行时

DBMS_SCHEDULER.STOP_JOB (
    job_name => 'MYJOB', 
    force => FALSE
);

调度程序中是否有一条特殊规则用于在 PL/SQL 块内的特殊点终止 PL/SQL 块?上例中,点“3”的DML有没有可能没有执行?

【问题讨论】:

  • 退出点在最后一行之后。
  • dbms_scheduler 的作业被创建并启用 - 因此运行。如果您的 Pl/SQL 块仍在执行,那么您将能够停止该作业,否则它将完成。检查 stop_job 过程的 force 选项,因为它需要特权,这实际上有点“杀死”工作。默认值为 force => false,它使用中断机制。
  • @a_horse_with_no_name - 我认为当您发出 stop_job 时,Toru 想知道它实际退出代码的位置。无论是否与 force 选项一起使用,它都会杀死从属进程或优雅地停止它。
  • 总是很难证明是否定的,但我很确定 DBMS_SCHEDULER 没有特殊能力,并且中断将与命令行上的 Control-C 相同,即它会在何时以及何时执行PL/SQL 运行时引擎感觉就是这样。

标签: oracle plsql jobs job-scheduling


【解决方案1】:

您正在调用 DBMS_SCHEDULER.STOP_JOB () 并将 force 设置为 false。根据文档,这意味着它将尝试“使用中断机制优雅地停止作业”。没有关于这种“中断机制”的详细信息,互联网上的网站只是简单地重复文档,没有任何进一步的见解。

让我们看看会发生什么。这是一个模仿你的程序。当它运行到完成时,T42 中将有 32 条记录。

create or replace procedure p_job_tst is
    n simple_integer := 1;
begin
    delete from t42;
    insert into t42 (stage, id) values (1, n);
    commit;
    for i in 1..30 loop
        n := n+1;
        insert into t42 (stage, id) values (2, n);
        commit;
        dbms_lock.sleep(2);
    end loop;   
    n := n+1;
    insert into t42 (stage, id) values (3, n);
    commit;
end;
/

dbms_lock.sleep() 的调用意味着它会执行一分钟,这使我们可以在运行中终止工作,而不必非常快地打字。这是工作:

BEGIN
    DBMS_SCHEDULER.CREATE_JOB (
       job_name             => 'apc.my_job1',
       job_type             => 'PLSQL_BLOCK',
       job_action           => 'BEGIN p_job_tst; END;',
       start_date           => systimestamp + interval '10' second,
       repeat_interval      => 'FREQ=MINUTELY;INTERVAL=3',
       enabled              =>  TRUE,
       comments             => 'StackOverflow Test');
END;
/

已经开始工作,让我们等到它运行,然后停止它:

SQL> select job_name, state, run_count from dba_scheduler_jobs where job_name = 'MY_JOB1';

JOB_NAME                       STATE            RUN_COUNT
------------------------------ --------------- ----------
MY_JOB1                        SCHEDULED                2

SQL> select job_name, state, run_count from dba_scheduler_jobs where job_name = 'MY_JOB1';

JOB_NAME                       STATE            RUN_COUNT
------------------------------ --------------- ----------
MY_JOB1                        RUNNING                  2

SQL> exec sys.dbms_scheduler.STOP_JOB(job_name=>'apc.my_job1', force=>false);

PL/SQL procedure successfully completed.

SQL> select job_name, state, run_count from dba_scheduler_jobs where job_name = 'MY_JOB1';

JOB_NAME                       STATE            RUN_COUNT
------------------------------ --------------- ----------
MY_JOB1                        SCHEDULED                3

SQL> 

在我停止工作之前,T42 有十条记录。 T42目前的内容?

SQL> r
  1* select * from t42 order by stage, id

        ID      STAGE
---------- ----------
         1          1
         2          2
         3          2
         4          2
         5          2
         6          2
         7          2
         8          2
         9          2
        10          2
        11          2

        ID      STAGE
---------- ----------
        12          2

12 rows selected.

SQL> 

所以作业被优雅地停止了,但不是在程序结束时。似乎中断机制正在寻找一个没有锁或长时间运行的进程的简洁断点。就像你的代码一样,我的代码有很多这样的断点。每个commit 结束一个事务并释放所有锁。

从调度程序的角度来看,这很好,因为“优雅地”停止工作似乎很容易。当然,从应用程序的角度来看,这完全是一场噩梦。该作业将在未知状态中结束。部分工作将已经完成并已提交,而有些部分将丢失。没有简单的方法可以判断每种有多少,同时您的数据库将处于潜在的不一致状态。

有与发出commit 语句相关的最佳实践:commit 一次,在工作单元结束时。后台作业是一个工作单元,因此它应该恰好有一个 commit 语句,就在终止 end 之前(假设您的块没有异常处理程序部分,它应该有)。

【讨论】:

    【解决方案2】:

    就个人而言,我会检查您的更新是否在循环中 [即row-at-a-time = slow-at-a-time] 可以重新编码为基于集合的更新。毕竟,Oracle RDBMS 被设计成一个非常高效的集合处理引擎;一次一行的解决方案几乎总是无法执行基于集合的解决方案并且不可扩展。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-05-06
      • 2017-01-01
      • 2021-12-03
      • 2019-09-13
      • 2017-02-05
      • 1970-01-01
      • 2021-07-05
      相关资源
      最近更新 更多