2

我在这个FIDDLE中有这样的表:

CREATE TABLE buffor_table
    ("ID" int, "NAME" varchar2(10), "STATUS" varchar2(10))
;

INSERT ALL 
    INTO buffor_table ("ID", "NAME", "STATUS")
         VALUES (1, 'something1', 'PROCESSING')
    INTO buffor_table ("ID", "NAME", "STATUS")
         VALUES (2, 'something2', 'WAITING')
    INTO buffor_table ("ID", "NAME", "STATUS")
         VALUES (3, 'something3', 'WAITING')
    INTO buffor_table ("ID", "NAME", "STATUS")
         VALUES (4, 'something4', 'FINISHED')
SELECT * FROM dual
;

CREATE OR REPLACE VIEW V_BUFFOR_WAITING AS
  SELECT ID, NAME, STATUS FROM BUFFOR_TABLE
  WHERE STATUS = 'WAITING';

我需要有一些流程/作业,每分钟都会查找处于“WAITING”状态的行,然后每一个都跨越新的子流程来完成这项工作。

我想提供这个解决方案;创建 10 个链(使用 Oracle 链)并将它们设置为并行执行,并且每分钟触发一次 oracle 作业。链逻辑将很简单:

  1. select id into id_to_process from v_buffor_waiting where rownum < 2 for update;
  2. update buffor_table set status='PROCESSING' where id = id_to_process;
  3. commit
  4. 做一些长时间的计算等
  5. 用于将状态设置为的简单更新/提交操作FINISHED

我知道NO_DATA_FOUND可以在位置 1 抛出异常。它将被视为“无事可做”。

我的步骤 1-3 仅锁定一行(当启动 10 个并行链时,我希望每个链只处理一行),步骤 1-3 将类似于原子操作 - 没有其他进程将具有相同的 ID 来处理。

有没有解决这个问题的标准方法?

4

1 回答 1

1

这是一个典型的调度场景。Oracle 非常擅长并行运行 SQL 查询和更新,但这可能更难。我过去曾在三个使用 Oracle 作为后端的不同调度程序上工作过。

我的一些经验建议:

  • 使用表,而不是视图,因为视图没有锁定触发器(您已经做得很好)。
  • 使用带有超时的“for update”来避免永久锁定。
  • 请记住,多处理器系统中的 Oracle 使用旋转而不是休眠来获取数据库锁。
  • 通过确保所有并发进程遵循相同的顺序来避免“用餐哲学家”问题。这避免了死锁。
  • 在每次尝试获取锁后添加睡眠。
  • 确保在事件(例如数据库被关闭)之后释放锁。例如,通过在update语句中注册 buffor_table 中的 sid/serial#。并自动释放锁。
  • 确保在链崩溃后,锁被释放。同样使用 sid/serial# 或类似的东西来检测消费进程是否还活着。
  • 作为替代方案,请查看dbms_lock. 它有一些限制,但可能会提供更简单但不太灵活的解决方案。

我希望这回答了你的问题。如果没有,请扩展您的问题或让我知道您缺少什么。

于 2014-01-31T17:30:36.277 回答