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