【问题标题】:Oracle update skip locked with dependency on other rowsOracle 更新跳过锁定并依赖于其他行
【发布时间】:2017-11-16 05:16:53
【问题描述】:

对于我们应用程序中的多线程环境,我们实现了 oracle 跳过锁定,其中没有两个线程在数据库中获取相同的记录进行处理(我们将“等待”添加到“工作”标志)。

现在,我们有一个修改,如果数据库中排队等待处理的两条记录具有相同的 ID(workid),则不应同时获取。(即,如果其他记录状态不应更新为 WORKING已经有一条记录有一个标志是“正在工作” 有人可以帮忙看看如何实现吗?

以下是相同的过程,其中单个记录被锁定而不进行比较。

create or replace PROCEDURE DEQUEUE_MANAGER(
    v_managerName IN Queue.manager%TYPE,
    v_workid IN VARCHAR2,
    v_key OUT NUMBER,
    v_datatablekey OUT Queue.DATA_TABLE_KEY%TYPE,
    v_tasktype OUT Queue.TASK_TYPE%TYPE,
    v_no_of_attempts OUT Queue.ATTEMPT%TYPE,
    v_result OUT NUMBER,
    v_error_desc OUT VARCHAR2)
IS
    v_recordrow Queue%ROWTYPE;
    v_qeuuestatus  VARCHAR2(255);
    v_updatedstaus VARCHAR2(255);
    CURSOR c IS 
        select * 
        from QUEUE  
        WHERE MANAGER =v_managerName 
        AND STATUS =v_qeuuestatus 
        AND workid=v_workid 
        AND DATE_RELEASE<=SYSDATE 
    FOR UPDATE SKIP LOCKED;
BEGIN
    v_result      := -1;
    v_qeuuestatus :='WAITING';
    v_updatedstaus:='WORKING';
    v_tasktype :='';
    v_datatablekey:=-1;
    v_key:=-1;
    v_error_desc:='No Data Found';
    v_no_of_attempts:=0;
    OPEN c;
    FOR i IN 1..1 LOOP
        FETCH c INTO v_recordrow;
        EXIT WHEN c%NOTFOUND;
        select v_recordrow.key into v_key 
        from QUEUE 
        where key = v_recordrow.key 
        for update;

        UPDATE Queue 
        SET STATUS=v_updatedstaus 
        WHERE KEY=v_recordrow.key;
        COMMIT;
        v_datatablekey:=v_recordrow.data_table_key;
        v_tasktype    := v_recordrow.task_type;
        v_no_of_attempts := v_recordrow.attempt;
        v_result      := 0;
        IF (v_no_of_attempts IS NULL) THEN
            v_no_of_attempts:=0;
        END IF;
    END LOOP;
    CLOSE c;
EXCEPTION
    WHEN NO_DATA_FOUND THEN
            v_datatablekey:=-1;
            v_tasktype:='';
            v_key:=-1;
            v_no_of_attempts:=0;
            v_result    :=-1;
            v_error_desc:='No Rows Found';      
    WHEN OTHERS THEN
              DBMS_OUTPUT.put_line ('Exception Occurred');
              v_datatablekey:=0;
              v_tasktype:='';
              v_key:=0;
              v_no_of_attempts:=0;
              v_result     := -2;
              v_error_desc := SQLERRM;
              ROLLBACK;
END;

【问题讨论】:

  • @shmosel 已编辑
  • workid的相同值可以有queue statusmanager的不同值吗?这对锁定规则有影响吗?
  • @APC 所有值最初都是相同的。我们只是将状态从“WAITING”更新为“WORKING”,这样记录就不会再次被选中并表明它已经在工作了。我没有锁定规则。

标签: multithreading oracle oracle11g


【解决方案1】:

FOR UPDATE 语法的目的是锁定我们希望在未来某个时间更新的记录。我们现在想获取记录,这样我们就可以确保我们的后续更新不会因为另一个会话锁定记录而失败。

这不是您的代码所做的。相反,它选择记录,更新它,然后发出提交。提交结束事务,释放锁。如果没有 FOR UPDATE,您的流程也可以正常工作。

现在我们有您的额外要求:如果正在处理给定 workid 的排队记录,则另一个会话不能处理同一 workid 的其他记录。您说workid 的所有实例都具有相同的queue statusmanager 值。这意味着最初的 SELECT FOR UPDATE 会抓取您想要锁定的所有记录。问题是 SKIP LOCK 允许其他会话更新该workid 的任何其他记录(实际上只有第一条记录被锁定,因为这是您唯一更新的记录)。 sd 提交释放这些锁并不重要。

最简单的解决方案是删除 SKIP LOCKED 和 COMMIT。这将使所有相关记录保持锁定,直到您的处理事务提交。但这可能会在其他地方造成更多问题。

并发编程真的很难做到。这是一个架构问题;您无法在单个程序单元的级别上解决它。

【讨论】:

  • 并发编程真的很难做到正确。这是一个架构问题;您无法在单个程序单元的级别上解决它。 确实。该存储过程看起来像是试图掩盖一个基本的设计问题。为什么首先将 RDBMS 用作某种排队机制?为什么一个极其复杂的存储过程甚至被认为是必要的?根据我的经验,添加这样的代码是为了解决问题的症状:“这不起作用,所以我们需要ADD 更多代码。”通常它不会很好地工作。
  • @andrewherle - 我认为数据库有一个队列来管理其后台进程是完全合理的。为什么不?但是,当人们实施自己的排队机制而不是使用 Oracle 的内置 AQ 解决方案时,我有疑问。这通常表明存在更广泛的问题(缺乏 Oracle 经验、好斗的 DBA、自负的架构师)。 YMMV
  • @APC 我可能不得不重新考虑架构。用例是我们有一个多线程 Java 程序,它从队列和进程中获取一条记录。使用代码同步,因此没有两个线程有​​相同的记录导致死锁。为了解决这个问题,我们创建了一个如上所述的 oracle 跳过锁定过程,让 oracle 处理它,这就是跳过锁定的原因。因为一个线程被分配了一条记录,所以它一次更新一条记录并返回rowId进行处理。在上下文中,我相信这个要求现在我们必须考虑一个可行的解决方案,而不是增加复杂性
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-13
  • 2013-09-18
  • 2018-02-20
  • 2021-02-26
  • 1970-01-01
  • 2015-09-19
  • 2015-10-18
相关资源
最近更新 更多