デッドロックの仕組みを図解で説明
〜排他制御とロックの種類〜
「デッドロック」知っていますか?なぜ起きるのか説明できますか?
言葉は聞いたことがあっても、処理の順番まで理解できていない人も多いと思います。
排他制御とデッドロックの仕組みについて、図を使って説明していきます。
排他制御とは?
排他制御は、複数の処理が同じデータを同時に更新して、データがおかしくならないように制御する仕組みです。
システムには、たくさんの利用者が同時にアクセスします。
もし何も制御をしていないと、次のような問題が起きます。
排他制御がないとどうなるのか
商品の在庫が10個あり、AさんとBさんが同時に1個ずつ購入する場合を考えます。
①Aさんの処理が在庫を読み込む(10個)
②Bさんの処理が在庫を読み込む(10個)
③Aさんの処理が在庫を9個に更新する
④Bさんの処理が在庫を9個に更新する

2個売れたのに、在庫は9個になっています。
Aさんの更新が、Bさんの更新で上書きされてしまったのです。これを「更新の消失(ロストアップデート)」といいます。
これを防ぐために、データを読んでから更新が終わるまで、他の処理が触れないようにします。この仕組みが「ロック」です。
ロックの種類
ロックには、大きく2つの種類があります。
共有ロック
共有ロックは、データを読むときにかけるロックです。
他の処理も同じデータを読むことはできますが、更新はできません。
専有ロック
専有ロックは、データを更新するときにかけるロックです。「排他ロック」と呼ぶこともあります。
他の処理は、読むことも更新することもできません。
ロックの組み合わせ
すでにロックがかかっているデータに、後から別の処理がロックをかけられるかは次のようになります。

共有ロック同士の場合だけ、同時にロックをかけることができます。
それ以外の場合、後から来た処理は先の処理が終わるまで待つことになります。
試験では、共有ロック同士だけが同時に可能と覚えておけば迷いにくくなります。
ここでの説明は試験で問われる基本の動きです。実際の製品では、更新中でも更新前のデータを読める仕組み(MVCCなど)を持つものが多くあります。
デッドロックとは?
デッドロックは、2つ以上の処理がお互いのロックの解除を待ち続けて、どちらも進めなくなる状態です。
処理Xと処理Yが、データAとデータBを更新する場合を考えます。
①処理XがデータAをロック
②処理YがデータBをロック
③処理XがデータBをロックしようとして、処理Yの終了を待つ
④処理YがデータAをロックしようとして、処理Xの終了を待つ

処理Xは処理Yを待っていて、処理Yは処理Xを待っています。
お互いに相手が終わるのを待っているので、どちらも先に進めません。
ポイントは、2つの処理がデータを逆の順番でロックしていることです。
デッドロックの対策
デッドロックを防ぐ、または起きにくくするには、次のような方法があります。
- ロックする順番をそろえる:すべての処理でデータA、データBの順番にする
- 処理を短くする:ロックしている時間を短くする
- ロックする範囲を小さくする:表全体ではなく、必要な行だけをロックする
最も分かりやすいのは、ロックする順番をそろえることです。
先ほどの例で処理Yも先にデータAをロックすれば、処理XがデータAを使っている間、処理Yは待つだけです。処理Xが終われば、処理Yは順番に進めることができます。
なお、同じデータでも、2つの処理が共有ロックで読んでから専有ロックに変えようとすると、デッドロックになることがあります。

また、多くのデータベースはデッドロックを検知する機能を持っています。
検知すると片方の処理をエラーにして取り消し(ロールバック)、もう片方の処理を進めます。
そのため、アプリ側では取り消された処理をやり直す仕組みも考えておく必要があります。
データベースの基本については、データベースの種類を図解で解説でも説明しています。
まとめ
今回は排他制御とデッドロックの仕組みについて説明しました。
排他制御は、同時に更新してもデータがおかしくならないようにする仕組みです。そのためにロックを使います。
デッドロックは、ロックをかけた処理同士がお互いを待ち続けてしまう状態です。
基本情報技術者試験では、処理の順番を追いかけることが大切です。図を書きながら、どの処理がどのデータをロックしているのかを確認してみましょう。
実際のシステムでも、利用者が増えた時に突然デッドロックが起きることがあります。設計する時は、どの順番でデータを更新するのかを意識しておきましょう。


