Mistake #74 : Database Deadlock

DBが沈黙する
「デッドロックの抱擁」

「お前が先に離せ」「いや、お前こそ先に離せ」。
2つの処理が互いに相手の持っているリソースを要求し合い、永遠に動かなくなる状態。
それは西部劇の決闘(Mexican Standoff)のように、誰も引き金を引けないまま、システム全体をフリーズさせます。

🔐 膠着状態診断
  • トランザクションAとBで、テーブルを更新する順番がバラバラだ
  • 「楽観ロック」という言葉を知らない、あるいは使っていない
  • バッチ処理とオンライン処理が、同じテーブルを同時に更新している
  • `SELECT FOR UPDATE` をとりあえず書いておけば安全だと思っている
⚠️ 警告:デッドロックは「高負荷時」にのみ発生します。
開発環境では再現せず、リリース初日のアクセス集中時にシステムを即死させます。

01. 終わらない睨み合い

WAITING...

デッドロックの原理はシンプルです。
Aは「在庫」をロックして「注文」を書き込もうとする。
Bは「注文」をロックして「在庫」を減らそうとする。
この時、タイミングが悪いと、AはBのロック解除を待ち、BはAのロック解除を待つ状態になります。
DBMSが強制終了(Victim選定)するまで、この状態は続き、他のすべて処理を巻き込んで詰まらせます。

ロックの順番を統一しろ。
例外なく、だ。

02. 順序の強制

デッドロックを防ぐ最も確実な方法は、「リソースにアクセスする順序を全システムで統一する」ことです。
「必ず親テーブル→子テーブルの順でロックする」
「必ずIDの昇順でロックする」
このルールを徹底するだけで、円環(Cycle)が生まれなくなり、デッドロックは物理的に発生しなくなります。

03. 楽観的ロックの活用

そもそも、DBのロック(悲観的ロック)を使わないアプローチもあります。
データに `version` カラムを持たせ、更新時にバージョンが一致しているか確認する(楽観的ロック)。
これならDBロックを保持する時間がゼロになり、デッドロックの可能性を根本から絶つことができます。

執筆:トランザクション設計士

並行処理の指揮者。
数千TPSの高負荷システムにおいて、一貫性を保ちながらスループットを最大化するロック設計を行う。