我在Postgres数据库中看到一些无法解释的死锁。简化相关查询,死锁中涉及的一个事务是:
BEGIN;
UPDATE A SET CHUNK_ID=1, STATUS='PROCESSING' WHERE ID IN (
SELECT ID FROM A
WHERE CHUNK_ID IS NULL
ORDER BY O_ID
LIMIT 1000
FOR UPDATE
);
COMMIT;
另一个是:
BEGIN;
UPDATE A SET STATUS='SENT' WHERE ID = 1;
UPDATE A SET STATUS='SENT' WHERE ID = 2;
UPDATE A SET STATUS='SENT' WHERE ID = 3;
...
COMMIT;
我的问题是如何在这里陷入僵局?我无法想到第一个事务可能导致死锁的任何情况,无论同时运行任何其他查询。
是否存在任何此类情况,即使用嵌套SELECT的UPDATE ... FOR UPDATE可能是死锁的一部分?
由于
答案 0 :(得分:0)
(这是一个猜想,但希望是受过教育的猜想。)
所有内容都取决于SELECT ... ORDER BY O_ID ... FOR UPDATE锁定行的顺序。如果O_ID的顺序与ID的顺序不同,那么完全有可能出现类似这样的情况:
ID O_ID
-- ----
1 2
2 1
警告:即使O_ID顺序与ID顺序相同,ORDER BY子句也可能实际上不保证锁定顺序(它只保证返回结果的顺序)。不幸的是,这似乎记录不清楚。对于它的价值,似乎Oracle在锁定时并不(总是)尊重ORDER BY,所以我在PostgreSQL下也要小心。
通常,死锁的解决方案是始终以相同的顺序锁定。假设ORDER BY实际上保证了锁定的顺序,你可以在第二个事务中简单地包括SELECT ... ORDER BY O_ID ... FOR UPDATE。或者,在第一个事务中使用ORDER BY ID。
顺便说一句,你为什么要明确锁定?你到底想要完成什么?