全部术语

死锁

PostgreSQL 中的死锁是什么?如何避免?

当两个事务各自等待对方持有的锁时,就会发生死锁。PostgreSQL 会检测到死锁并取消其中一个事务;按一致的顺序加锁可以预防死锁。

死锁是由锁等待形成的环:事务 A 持有 B 需要的锁,而 B 持有 A 需要的锁,双方都无法继续。PostgreSQL 会自动检测这种环,并通过中止其中一个事务来解除死锁,被中止的事务会收到 deadlock detected 错误。

这项检测并不会在每次锁等待时都运行:会话会先等待 deadlock_timeout(默认为 1 秒),然后才检查是否存在环。最好的防御方法是在每个事务中按相同的顺序锁定相同的对象,例如按主键排序后再锁定行,并让事务尽量简短。仍有可能遇到死锁的代码应重试被中止的事务。

示例

该示例在更新之前,先用一条语句按 id 排序锁定即将修改的两行。当所有涉及这些行的事务都这样做时,它们会排队等待,而不会发生死锁。

CREATE TABLE ledger (id int PRIMARY KEY, balance int);
INSERT INTO ledger VALUES (1, 100), (2, 100);

SHOW deadlock_timeout;

BEGIN;
SELECT id FROM ledger WHERE id IN (1, 2) ORDER BY id FOR UPDATE;
UPDATE ledger SET balance = balance - 10 WHERE id = 1;
UPDATE ledger SET balance = balance + 10 WHERE id = 2;
COMMIT;

适用于原生 PostgreSQL 16 或 17 的纯 SQL,无需任何扩展。请在一个可随时丢弃的空数据库中运行。

参考资料

PostgreSQL 官方文档(英文)。

相关术语