DBが沈黙する
「デッドロックの抱擁」
「お前が先に離せ」「いや、お前こそ先に離せ」。
2つの処理が互いに相手の持っているリソースを要求し合い、永遠に動かなくなる状態。
それは西部劇の決闘(Mexican Standoff)のように、誰も引き金を引けないまま、システム全体をフリーズさせます。
- トランザクションAとBで、テーブルを更新する順番がバラバラだ
- 「楽観ロック」という言葉を知らない、あるいは使っていない
- バッチ処理とオンライン処理が、同じテーブルを同時に更新している
- `SELECT FOR UPDATE` をとりあえず書いておけば安全だと思っている
開発環境では再現せず、リリース初日のアクセス集中時にシステムを即死させます。
01. 終わらない睨み合い
デッドロックの原理はシンプルです。
Aは「在庫」をロックして「注文」を書き込もうとする。
Bは「注文」をロックして「在庫」を減らそうとする。
この時、タイミングが悪いと、AはBのロック解除を待ち、BはAのロック解除を待つ状態になります。
DBMSが強制終了(Victim選定)するまで、この状態は続き、他のすべて処理を巻き込んで詰まらせます。
例外なく、だ。
02. 順序の強制
デッドロックを防ぐ最も確実な方法は、「リソースにアクセスする順序を全システムで統一する」ことです。
「必ず親テーブル→子テーブルの順でロックする」
「必ずIDの昇順でロックする」
このルールを徹底するだけで、円環(Cycle)が生まれなくなり、デッドロックは物理的に発生しなくなります。
03. 楽観的ロックの活用
そもそも、DBのロック(悲観的ロック)を使わないアプローチもあります。
データに `version` カラムを持たせ、更新時にバージョンが一致しているか確認する(楽観的ロック)。
これならDBロックを保持する時間がゼロになり、デッドロックの可能性を根本から絶つことができます。