考虑以下示例:
-- Transaction 1 -> T1
BEGIN;
SELECT * FROM table1 WHERE id = 1 FOR UPDATE;
SELECT * FROM table1 WHERE id = 2 FOR UPDATE;
UPDATE table1 set col1 = 'abcd' where id = 1;
COMMIT;
-- Transaction 2 -> T2
BEGIN;
SELECT * FROM table1 WHERE id = 2 FOR UPDATE;
SELECT * FROM table1 WHERE id = 1 FOR UPDATE;
UPDATE table1 set col1 = 'defg' where id = 2;
COMMIT;
在这个例子中,很明显如果两个事务同时执行,死锁可能会发生,因为如果T1锁定id=1的行,然后T2锁定id=2的行,那么T1和T2都无法执行第二个SELECT FOR UPDATE查询,从而导致死锁。
现在,为了解决这个问题,我们可以按照相同的顺序执行SELECT FOR UPDATE查询:
-- Transaction 1 -> T1
BEGIN;
SELECT * FROM table1 WHERE id = 1 FOR UPDATE;
SELECT * FROM table1 WHERE id = 2 FOR UPDATE;
UPDATE table1 set col1 = 'abcd' where id = 1;
COMMIT;
-- Transaction 2 -> T2
BEGIN;
SELECT * FROM table1 WHERE id = 1 FOR UPDATE;
SELECT * FROM table1 WHERE id = 2 FOR UPDATE;
UPDATE table1 set col1 = 'defg' where id = 2;
COMMIT;
我们解决了这个例子的死锁问题。
现在,我的问题是,如果你考虑以下类似的例子:
-- Transaction 1 -> T1
BEGIN;
SELECT * FROM table1 WHERE id IN (1, 2) FOR UPDATE;
UPDATE table1 set col1 = 'abcd' where id = 1;
COMMIT;
-- Transaction 2 -> T2
BEGIN;
SELECT * FROM table1 WHERE id IN (1, 2) FOR UPDATE;
UPDATE table1 set col1 = 'defg' where id = 2;
COMMIT;
我的问题:
最后一个例子中是否可能出现死锁?
换句话说,Postgres 是否会原子地同时锁定与 WHERE 条件匹配的所有行?
如果是,则我们能否 还 说 WHERE 子句的顺序不重要?因此在 T1 中我们可以使用:
SELECT * FROM table1 WHERE id IN (1, 2) FOR UPDATE;
在 T2 中我们可以使用:
SELECT * FROM table1 WHERE id IN (2, 1) FOR UPDATE;
如何避免发生死锁?