Oracle更新跳过锁定,依赖于其他行

时间:2017-11-16 05:16:53

标签: multithreading oracle oracle11g

对于我们应用程序中的多线程env,我们实现了oracle skip locked,其中没有两个线程在数据库中拾取相同的记录进行处理(我们将'waiting'添加到'working'标志)。

现在,我们有一个修改,如果数据库中排队等待处理的两个记录具有相同的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;

1 个答案:

答案 0 :(得分:1)

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

这不是你的代码所做的。相反,它选择记录,更新它,然后发出提交。提交结束事务,释放锁。如果没有FOR UPDATE,你的过程就会一样。

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

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

并发编程真的很难做到。这是一个建筑问题;你无法在单个程序单元的层面上解决它。