Multithreading Oracle更新跳过锁定,与其他行相关

Multithreading Oracle更新跳过锁定,与其他行相关,multithreading,oracle,oracle11g,Multithreading,Oracle,Oracle11g,对于应用程序中的多线程env,我们实现了oracle skip locked,其中没有两个线程在数据库中拾取相同的记录进行处理(我们将'waiting'添加到'working'标志) 现在,我们有一个修改,如果数据库中排队等待处理的两条记录具有相同的ID(workid),则不应同时拾取(即,如果已有一条记录具有“正在工作”的标志,则另一条记录的状态不应更新为“正在工作” 有人能帮助我们如何做到这一点吗 下面是锁定单个记录而不进行比较的相同步骤 create or replace PROCEDUR

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

现在,我们有一个修改,如果数据库中排队等待处理的两条记录具有相同的ID(workid),则不应同时拾取(即,如果已有一条记录具有“正在工作”的标志,则另一条记录的状态不应更新为“正在工作” 有人能帮助我们如何做到这一点吗

下面是锁定单个记录而不进行比较的相同步骤

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;
创建或替换过程出列管理器(
队列中的v_manager名称。管理器%TYPE,
v_在VARCHAR2工作,
v_密钥输出编号,
v_datatablekey OUT队列。数据_TABLE_KEY%类型,
v_任务类型输出队列。任务类型%TYPE,
v_no_of_尝试退出队列。尝试%TYPE,
v_结果输出编号,
v_错误\u描述输出VARCHAR2)
是
v_recordrow队列%ROWTYPE;
v_Qeuestatus VARCHAR2(255);
v_更新的Staus VARCHAR2(255);
光标c是
选择*
从队列
其中MANAGER=v_managerName
和状态=v_qeuuestatus
AND workid=v_workid

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

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

现在我们有了您的附加要求:如果正在处理给定
workid
的排队记录,则同一
workid
的其他记录不能由另一个会话处理。您说
workid
的所有实例都具有相同的
queue status
manager
值。这意味着initial SELECT FOR UPDATE会抓取所有要锁定的记录。问题是跳过锁定允许其他会话更新该
工作ID
的任何其他记录(只有第一条记录实际被锁定,因为这是您唯一更新的记录)。这并不重要,提交会释放这些锁定

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


并发编程确实很难正确进行。这是一个体系结构问题;您无法在单个程序单元的级别上解决它。

@shmosel edited相同的
workid
值是否可以有不同的
队列状态
管理器
?这对锁定规则有任何影响吗?@APC all-valUE最初是一样的。我们只是将状态从“等待”更新为“工作”,这样记录就不会再次被拾取,并表明它已经在工作。我没有锁定规则。并发编程很难正确进行。这是一个体系结构问题;你无法在单个程序单元的级别上解决它。事实上,这存储了procedure看起来像是试图掩盖一个基本的设计问题。为什么RDBMS首先被用作某种排队机制?为什么一个极其复杂的存储过程被认为是必要的?根据我的经验,添加这样的代码是为了解决问题的症状:“这不起作用,因此我们需要添加更多代码。“通常它不太好用。@Andrewerle-我认为一个数据库有一个队列来管理其后台进程是完全合理的。为什么不呢?但是当人们实现自己的队列机制而不是使用Oracle内置的AQ解决方案时,我有疑问。这通常意味着更广泛的问题(缺乏Oracle经验、好斗的DBA、傲慢的架构师). YMMV@APC我可能不得不重新考虑架构。用例是我们有一个多线程java程序,它从队列和进程中提取一条记录。使用代码同步,使两个线程没有相同的记录,从而导致死锁。为了解决这个问题,我们创建了一个oracle跳过锁定过程,如上所述,让oracle处理它这就是跳过被锁定的原因。因为一个线程被分配了一条记录,所以它一次更新一条记录并返回rowId进行处理。在这种情况下,我相信现在我们必须考虑一个可行的解决方案,而不是增加复杂性