【发布时间】:2020-10-25 11:16:27
【问题描述】:
如何释放另一个会话的行级锁以避免for update死锁? 我有一张桌子
这是代码,
DECLARE
CURSOR c1 IS
SELECT id
FROM tasks;
ls_id NUMBER;
vaf varchar2(1000);
BEGIN
OPEN c1;
LOOP
FETCH c1
INTO ls_id;
vaf := 'select * from tasks where id =''' || ls_id ||
''' for update of id';
EXECUTE IMMEDIATE vaf;
UPDATE tasks
SET status = 'B'
WHERE id = ls_id;
EXIT WHEN c1%NOTFOUND;
END LOOP;
CLOSE c1;
commit;
END;
如果我首先在一个会话中运行 for update 表 tasks where id=4 row,然后在 plsql 开发人员 IDE 测试中的另一个会话中运行上述块窗口并逐行执行。循环继续并在 id=4 处陷入死锁。这里我在循环之后提交,因此 id=1,2 和 3 将不会有更新。如果我在之后提交update 语句它将更新记录到 id = 3 并在 id = 4 处出现死锁。是的,将提交放在循环中并不是一个好主意。
所以为了避免死锁,我使用了跳过锁定。
SELECT id FROM tasks FOR UPDATE SKIP LOCKED;
这个解决了 id=4 的死锁,而是跳过锁定的记录 id=4 并更新剩余的记录。
不允许终止会话。还有其他可能的方法来更新锁定的记录吗? 因为我正在运行一个调度程序,它正在更新重要的 trans。所以我不需要避免或跳过锁定的记录,也不需要调度程序没有死锁。有可能吗?
【问题讨论】:
-
您要更新的列也是外键列吗?如果是这样,该列是否已编入索引?如果没有,请尝试在其上创建索引。根据您的数据库版本,锁定发生在未索引的外部列上。也许它会有所帮助并且不会花费太多尝试。
-
“还有其他可能的方法来更新锁定的记录吗?” 没有。另一个会话有锁,在该会话释放它之前您不能触摸该记录。如果您提供有关您尝试实现的业务逻辑的更多详细信息,我们可能会提供一些建议。事实上,我们所能做的就是以一种困惑的方式查看您的代码。
标签: oracle cursor deadlock commit database-deadlocks