Postgres僵局

时间:2013-02-19 18:20:56

标签: sql postgresql-9.2 database-deadlocks

我在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可能是死锁的一部分?

由于

1 个答案:

答案 0 :(得分:0)

(这是一个猜想,但希望是受过教育的猜想。)

所有内容都取决于SELECT ... ORDER BY O_ID ... FOR UPDATE锁定行的顺序。如果O_ID的顺序与ID的顺序不同,那么完全有可能出现类似这样的情况:

ID    O_ID
--    ----
1     2
2     1
  • 事务A锁定ID = 2的行。
  • 事务B锁定ID = 1的行。
  • 事务A尝试锁定ID = 1的行,但强制在事务B上等待。
  • 事务B尝试锁定ID = 2的行,但强制等待事务A。

警告:即使O_ID顺序与ID顺序相同,ORDER BY子句也可能实际上不保证锁定顺序(它只保证返回结果的顺序)。不幸的是,这似乎记录不清楚。对于它的价值,似乎Oracle在锁定时并不(总是)尊重ORDER BY,所以我在PostgreSQL下也要小心。

通常,死锁的解决方案是始终以相同的顺序锁定。假设ORDER BY实际上保证了锁定的顺序,你可以在第二个事务中简单地包括SELECT ... ORDER BY O_ID ... FOR UPDATE。或者,在第一个事务中使用ORDER BY ID。

顺便说一句,你为什么要明确锁定?你到底想要完成什么?