PostgreSQLデッドロックの原因と対処法
冒頭まとめ PostgreSQLで次のエラーが出た場合、複数のトランザクションが互いのロック解放を待っています。 ERROR: deadlock detected DETAIL: Process 1234 waits for ShareLock on transaction 5678; blocked by process 4321. Process 4321 waits for ShareLock on transaction 5679; blocked by process 1234. HINT: See server log for query details. 最も重要な対策は、複数の行や表を更新する順序をすべての処理で統一することです。そのうえで、SQLSTATE 40P01を検出したらトランザクション全体を再試行します。 deadlock_timeoutを長くしたり短くしたりしても、デッドロックの原因そのものはなくなりません。まずサーバーログで衝突した問い合わせを特定し、ロックを取る順序を直してください。 deadlock detectedとは デッドロックは、2つ以上のトランザクションが互いに必要なロックを持ち、どちらも先へ進めなくなった状態です。 たとえば、トランザクションAが行1を更新してから行2を待ち、トランザクションBが行2を更新してから行1を待つと、待ち合わせの輪ができます。PostgreSQLはこの状態を自動で検出し、関係するトランザクションの1つを中断して処理を進めます。 どのトランザクションが中断されるかは予測しにくく、アプリケーション側で決めつけてはいけません。明示的にLOCK TABLEを使っていなくても、通常のUPDATEが取得する行ロックだけで発生します。これらの動作はPostgreSQL公式文書のDeadlocksで説明されています。 このエラーのSQLSTATEは40P01、条件名はdeadlock_detectedです。アプリケーションでは文章ではなくSQLSTATEで判定すると、表示言語や文言の変化に影響されにくくなります。PostgreSQLのエラーコード一覧でも40P01を確認できます。 最初にサーバーログを確認する 手元のエラーに表示されるDETAILには、待っているプロセス番号、ロックの種類、トランザクション番号などが並びます。ただし、衝突した問い合わせ文がクライアント側の出力に含まれない場合があります。 HINT: See server log for query details.と表示されたら、PostgreSQLのサーバーログを確認してください。PostgreSQL本体は、クライアント向けの詳細とログ向けの詳細を分けて組み立て、ログ側には各プロセスの問い合わせ文を追加します。この処理はPostgreSQL本体のdeadlock.cで確認できます。 ログには次のような情報が記録されます。 Process 1234: UPDATE accounts SET balance = balance - 100 WHERE acctnum = 22222; Process 4321: UPDATE accounts SET balance = balance + 100 WHERE acctnum = 11111; プロセス番号だけを見て原因を決めず、各問い合わせがどの順序で行や表を操作しているかを比べます。 ...