はじめに
同時並行的に動く複数の処理でデータベースを更新する際、データベースの更新結果が不正になるケースがあります。
それを防ぐ上で、ロック(排他制御)を用いた制御を行うことが有効です。
しかし、ロックの使い方を誤ると、更新処理が先に進まなくなるデッドロックと呼ばれる現象が発生することがあります。
この記事では、ロックとデッドロックについて、一般的なことを図を用いながら説明します。
実務で使うだけでなく、情報処理技術者試験でも令和の時代に出題されたことがあるので、試験対策という意味でも見逃せないトピックです。
トランザクションとは
ロックやデッドロックを理解する前に、「トランザクション」という概念を理解する必要があります。
トランザクションとは、複数の処理をひとまとまりにしたものです。
例えば、出庫時に在庫数を更新するトランザクションは、下記の処理をひとまとまりにすることで実現できます。
- 在庫テーブルから在庫量を取得する
- 取得した在庫量から出庫した分を差し引く
- 差し引いた在庫量を在庫テーブルに反映させる
ロックとは
トランザクションを実行する上で注意が必要なのは、トランザクションで一連の処理を実行している間に、他のトランザクションで参照・更新対象のテーブルが更新されないことを保証する必要があることです。
先ほどの例で言うと、トランザクション実行中に在庫テーブルが更新されないことを保証する必要があります。
もし、在庫量を取得した後に在庫テーブルが更新されてしまった場合、トランザクションで算出した在庫量でその更新を上書きしてしまいます。
それを防ぐために、「ロック」(排他制御)と呼ばれる技術が用いられます。
ロックとは、参照・更新対象のテーブルに対して、他のトランザクションにより参照・更新されないように制御をかける(ロックもかけられないようにする)技術です。
流れとしては、「参照・更新対象のテーブルに対してロックをかける」→「トランザクションの一連の処理を行う」→「ロックを解除して他のトランザクションが参照・更新できる状態に戻す」のようになります。
先ほどの例では、トランザクション中で在庫テーブルにロック(排他制御)をかけるのが有効です。
それを図解すると以下の通りです。

デッドロックとは
ロックの使用には注意点があります。
複数のトランザクションがロックをかけた場合、複数のトランザクションがお互いにロック待ちの状態となり、トランザクションを終了できないためロックを解除することもできず、(強制的にトランザクションを終了させるまでの間)永久にトランザクションが終了しなくなる、という現象が発生することがあります。
このような現象を「デッドロック」と呼びます。
デッドロックの例を図で解説したものが以下のものになります。
- トランザクションA
- ①在庫テーブルをロック・更新する
- ②陳列テーブルをロック・更新する
- トランザクションB
- ①陳列テーブルをロック・更新する
- ②在庫テーブルをロック・更新する
この2つの同時にトランザクションが動くケースで、お互いの処理が②に達する前に①の処理を実行してしまうと、お互いに②の処理を実行することができなくなり、デッドロックの状態に陥ります。

デッドロックを防ぐ方法
デッドロックを防ぐための代表的な手段として、トランザクションの間でロックする順番を統一するという手段があります。
この手段を用いることで、他のトランザクションが中途半端に進んで中途半端にロックをかけることがなくなるので、デッドロックを防ぐことができるようになります。
先ほどの例では、トランザクションA・B共に在庫テーブルをロックしてから陳列テーブルをロックするという流れにすることで、以下の図のようにデッドロックを防ぐことができます。

あとがき
今回は、排他制御に関して、少し応用的な設計の知識を紹介しました。
弊社の書籍「絶対にJavaプログラマーになりたい人へ」では、ロックのかけ方について、具体的なコマンドを用いて説明しています。
環境構築の方法も含めて、手取り足取り説明していますので、実際に試してみたい方は是非とも手に取ってみてください!
株式会社サイゼントでは、即戦力のJavaプログラマーを育てるための書籍「絶対にJavaプログラマーになりたい人へ」をKindleで販売しています。

同じく、Spring Frameworkについてきめ細かく解説した別冊も販売中です。

また、上記の書籍をテキストとして用いたプログラミングスクール「サイゼントアカデミー」も開校しています。
このスクールは、受託開発事業・SES事業である弊社が、新入社員向けの研修で培ったノウハウを詰め込んだものです。

ご興味がある方は、上記画像から個別ページにアクセスしてみてください!
コメント