FE EXAM

DBのデッドロック(おたがいすくみ)

複数のトランザクションが互いのロック解放を待ち続けて処理が進まなくなる状態。

INTERACTIVE VISUALIZATION
保持
待ち
デッドロック
フェーズ
idle
A / B のロック
/
状態
正常
シナリオ
ステップ1 / 7
STEP 1/7開始前 — 2つのデータA・Bデッドロックは、複数のトランザクションが互いに相手のロック解放を待ち続けて、どちらも進めなくなる状態です。T1(=トランザクション1)とT2が、ともにデータAとデータBの両方を更新したい状況から始めます。
T1待機中T2待機中データA空きデータB空き保持待ち(要求)
解説

📌
DBデッドロックとは

T1T2互いに待つどちらも進めない

デッドロック(おたがいすくみ)とは、複数のトランザクションが互いに相手のロック解放を待ち続け、どちらも処理を進められなくなる状態のことです。誰も先に進めず、放っておくと永遠に止まったままになります。

身近な例で考えると、狭い一本道で出会った2台の車に似ています。どちらも「相手が先にどいてくれたら進む」と思って譲り合わずにいると、両方とも前に進めません。お互いが相手の行動を待っているせいで、永久に動けなくなります。

上のツールで▶ボタンを押すと、T1がデータA・T2がデータBを持ったまま互いに相手のデータを要求し、待ち合いになって停止する様子と、最後に一方を中断して解消する流れを確認できます。

🔁
発生条件

T1T2循環

デッドロックは、次の条件がそろったときに起こります。
各トランザクションがロックを保持したまま、別のロックも要求する
保持中のロックは奪い取れない(相手が自分から手放すのを待つしかない)
待ちの関係が輪(循環)になっている(T1→T2→T1のようにぐるりと一周する)

とくに大事なのが最後の循環待ちです。「AはBを待ち、BはAを待つ」のように待ちが一周してしまうと、誰も最初に手放せず、永遠に止まります。逆に言えば、この輪さえできなければデッドロックは起きません。

🛠️
検出・回避方法

デッドロックへの対処は、大きく「起きてから直す(検出)」と「そもそも起こさない(回避)」の2つに分かれます。

対処やり方
検出と解消待ちグラフの循環を見つけ、一方を強制中断(ロールバック)して輪を断ち切る
順序を統一全員がA→Bの順でロックを取ると決めると、循環ができず起きない
タイムアウト一定時間待っても取れなければあきらめて中断する
一括ロック必要なロックを最初にまとめて取り、途中で追加要求しない

実際のデータベースでは、待ちグラフを定期的に調べて循環を検出し、巻き戻しの負担が軽いほうのトランザクションを犠牲者として選んで中断する方法がよく使われます。中断されたトランザクションは取り消されたあと自動でやり直されるため、利用者には大きな影響が出にくくなっています。一番手軽な予防策は「全員がロックを取る順番をそろえる」ことです。

🔍
なぜ循環待ちになると進めないのか

① T1がAを取得してBを待つT1(A)B待ち② T2がBを取得してAを待つT2(B)A待ち③ T1はAを手放さない → T2も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→BT2: B→AT1がAを取った後、T2がBを取ってしまう→ 互いに相手のロックを待つ循環が完成○ 全員 A→B の順(循環は生まれない)T1: A→BT2: A→BT2はT1がAを解放するまで待つだけ

デッドロック回避の最もシンプルな方法は「全員が同じ順番でロックを取る」ことです。なぜこれで防げるのかを確認します。

順序がバラバラだとデッドロックになる理由は次のとおりです。
・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冊借りるとき「必ず番号が小さい本から先に借りる」とルールを決めるのに似ています。みんなが同じ順番でカウンターに並ぶので、誰かが先に並んでいたら後からきた人は素直に後ろに並ぶだけ。「お互いが相手の前に割り込もうとする」という状況がそもそも起きません。

練習問題

🎯
基本情報技術者 練習問題

Q1.データベースのデッドロックの説明として最も適切なものはどれか。
A.複数のトランザクションが互いに相手のロック解放を待ち続け、処理が進まなくなる状態
B.トランザクションの処理速度が極端に遅くなる状態
C.データベースのディスク容量が不足する状態
D.ネットワークが切断されてDBに接続できない状態
Q2.デッドロックが発生している典型的な状況はどれか。
A.T1がデータAを、T2がデータBをロックし、それぞれが相手の持つデータを要求して待っている
B.1つのトランザクションだけが動いている
C.すべてのトランザクションがロックを取らずに動いている
D.トランザクションがすべて完了している
Q3.デッドロックを解消する代表的な方法はどれか。
A.両方のトランザクションを永遠に待たせ続ける
B.一方のトランザクションを強制的に中断(ロールバック)して待ちの輪を断ち切る
C.データベースを再インストールする
D.すべてのロックを無視して書き込む

関連コンテンツ