用語集のすべての用語

デッドロック

PostgreSQL のデッドロックとは?回避する方法は?

デッドロックは、2 つのトランザクションが互いに相手の保持しているロックを待つことで発生します。PostgreSQL はこれを検出していずれか一方をキャンセルします。一貫した順序でロックを取得すれば防ぐことができます。

デッドロックとは、ロック待ちの循環です。トランザクション A が B の必要とするロックを保持し、B が A の必要とするロックを保持しているため、どちらも先に進めません。PostgreSQL はこのような循環を自動的に検出し、一方のトランザクションを中断して解消します。中断されたトランザクションは deadlock detected エラーを受け取ります。

このチェックはロック待ちのたびに実行されるわけではありません。セッションはまず deadlock_timeout(デフォルトは 1 秒)の間待機してから、循環を探します。最善の対策は、たとえば主キー順に並べて行をロックするなど、すべてのトランザクションで同じオブジェクトを同じ順序でロックし、トランザクションを短く保つことです。それでもデッドロックが起こりうるコードでは、中断されたトランザクションを再試行するようにしてください。

例

この例では、変更する予定の 2 行を、更新する前に id 順に並べて 1 つの文でロックします。これらの行に触れるすべてのトランザクションが同じようにすれば、デッドロックにならずに順番に待機します。

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 公式ドキュメント(英語)。

関連用語