複数のトランザクションが互いのロック解放を待ち続けて処理が進まなくなる状態。
デッドロック(おたがいすくみ)とは、複数のトランザクションが互いに相手のロック解放を待ち続け、どちらも処理を進められなくなる状態のことです。誰も先に進めず、放っておくと永遠に止まったままになります。
身近な例で考えると、狭い一本道で出会った2台の車に似ています。どちらも「相手が先にどいてくれたら進む」と思って譲り合わずにいると、両方とも前に進めません。お互いが相手の行動を待っているせいで、永久に動けなくなります。
上のツールで▶ボタンを押すと、T1がデータA・T2がデータBを持ったまま互いに相手のデータを要求し、待ち合いになって停止する様子と、最後に一方を中断して解消する流れを確認できます。
デッドロックは、次の条件がそろったときに起こります。
・各トランザクションがロックを保持したまま、別のロックも要求する
・保持中のロックは奪い取れない(相手が自分から手放すのを待つしかない)
・待ちの関係が輪(循環)になっている(T1→T2→T1のようにぐるりと一周する)
とくに大事なのが最後の循環待ちです。「AはBを待ち、BはAを待つ」のように待ちが一周してしまうと、誰も最初に手放せず、永遠に止まります。逆に言えば、この輪さえできなければデッドロックは起きません。
デッドロックへの対処は、大きく「起きてから直す(検出)」と「そもそも起こさない(回避)」の2つに分かれます。
| 対処 | やり方 |
|---|---|
| 検出と解消 | 待ちグラフの循環を見つけ、一方を強制中断(ロールバック)して輪を断ち切る |
| 順序を統一 | 全員がA→Bの順でロックを取ると決めると、循環ができず起きない |
| タイムアウト | 一定時間待っても取れなければあきらめて中断する |
| 一括ロック | 必要なロックを最初にまとめて取り、途中で追加要求しない |
実際のデータベースでは、待ちグラフを定期的に調べて循環を検出し、巻き戻しの負担が軽いほうのトランザクションを犠牲者として選んで中断する方法がよく使われます。中断されたトランザクションは取り消されたあと自動でやり直されるため、利用者には大きな影響が出にくくなっています。一番手軽な予防策は「全員がロックを取る順番をそろえる」ことです。
なぜ循環待ちが起きると永遠に止まるのか? — 鍵は「自分からロックを手放す条件」にあります。
ロックはトランザクションが自分で「使い終わった」と判断するまで手放しません。T1が「Bを取るまでAは手放さない」と考え、T2が「Aを取るまでBは手放さない」と考えると:
・T1はBが取れるまでAを手放さない(BはT2が持っている)
・T2はAが取れるまでBを手放さない(AはT1が持っている)
どちらも「相手が先に手放してくれれば自分が進める」という状態で停止します。
この「お互いが相手の行動を待っている」という関係が1周つながった輪(循環)になったとき、誰も最初の一手を踏み出せず、システムは完全に止まります。輪がつながっていない限り(たとえば T1 → T2 → T3 → T1 でも同じ)、どこかの誰かが先に手放せば崩れるはずですが、輪が完成していると出口がありません。
身近な例で考えると、「あなたが先にお金を払ったら私が品物を渡す」「あなたが先に品物を渡したら私がお金を払う」という状態です。どちらも「先に動いたら損」と思って止まります。仲裁者(=DBの検出機能)が来て一方を強制的にあきらめさせない限り、永遠に交渉は成立しません。
デッドロック回避の最もシンプルな方法は「全員が同じ順番でロックを取る」ことです。なぜこれで防げるのかを確認します。
順序がバラバラだとデッドロックになる理由は次のとおりです。
・T1が「A取得 → B取得」の順で動いていて、Aを持ったままBを待っている
・T2が「B取得 → A取得」の順で動いていて、Bを持ったままAを待っている
この2人の取得順が逆なので、T1がAを持ったままの間にT2がBを取ってしまい、循環が完成します。
では全員が「A→B」の順に統一するとどうなるか。
・T1もT2も「まずAを取ってからBを取る」と決まっている
・T1がAを取ったとき、T2もAを取ろうとする → T2はT1がAを手放すまで待つだけ
・T2はBを取っていないので、T1がBを取るのを邪魔しない
このとき「T2はAだけを待っている」ため、Bを持ったまま待つ状況がなくなります。循環が作れないので、デッドロックは発生しません。
身近な例で考えると、図書館で本を2冊借りるとき「必ず番号が小さい本から先に借りる」とルールを決めるのに似ています。みんなが同じ順番でカウンターに並ぶので、誰かが先に並んでいたら後からきた人は素直に後ろに並ぶだけ。「お互いが相手の前に割り込もうとする」という状況がそもそも起きません。