データベース

共有ロック、占有ロック、そしてデッドロックの仕組みを学ぼう!

2019年11月18日

データベースにおいてデータを一連の処理として行うことをトランザクションと呼びます。

例えば銀行の処理において、Aさんの口座からBさんの口座にお金を振り込む際、データベースの処理は「Aさんの口座の残高を減らし、Bさんの口座の残高を増やす」となります。

この処理をAさんの口座を減らすだけで、途中で止めてしまうと、お金が消えてしまい、大変なことになってしまいます。これを防ぐためにトランザクションという単位を管理する必要があります。

今回はこのトランザクション管理において、よく出てくるキーワードである「共有ロック、占有ロック、デッドロック」を解説します!

トランザクション管理とは?

各種ロックの前に、先ずはトランザクション管理を簡単に触れておきましょう!

トランザクションにはACID特性と呼ばれる以下の大事な4つの性質があり、これを守ることでデータを安全に保っております。

ACID特性

  • 原子性(ATOMICITY):トランザクションは、処理が完全に実行されるか、全く実行されないかどちらかであることを保証する
  • 一貫性(CONSISTENCY):トランザクションの実行前後で整合性を持って、一貫したデータであることを保証する
  • 独立性(ISOLATION):あるトランザクションの途中の状態が他のトランザクションへ影響することが無い
  • 耐久性(DURABILITY):一度データベースに登録されたデータは失われることが無い

この特性によって、データが正しく操作されているのですね。

排他制御の必要性

ここで大事なことを抑えましょう!

トランザクションが一つしか動いていない時は良いのですが、二つのトランザクションを同時に実行するとトランザクションが完了する間に同じデータを双方で更新してしまい、データそのものに矛盾が発生してしまうことがあります。

これを防ぐために一度に一つのトランザクションしかデータの更新が行えないように制御する仕組みがあります。これを「排他制御」と呼びます。そして、排他制御を行うための方法として、ロックがあります。

ロックは参照や更新するデータそのものにロック(鍵)をかけて、そのデータの仕様が終わったら解除する仕掛けです。

このロックの種類として、「共有ロック、占有ロック、デッドロック」があります。

では、これらのロックの違いを解説します。

共有ロックと占有ロックとは?

ロックの掛け方は二種類あります。

共有ロック

データを参照する時だけにかけるロックの方法です。

共有ロック中は他のトランザクションからもデータを参照することができますが、ロックしているので更新はできません

占有ロック

データを更新するときにかけるロックの方法です。

占有ロック中は他のトランザクションでデータを参照することもできなくなります。つまり、そのトランザクションだけが触れるようになる状態です。

複数のトランザクションが共有ロックや占有ロックをかけようとした時の処理は以下の表のようになります。

共有ロック、占有ロックの関係

デッドロックとは?

「デッドロック」って聞くと何だか怖い言葉に思えますね...そう恐いんです!

英語だと「行き詰まり、手詰まり、膠着状態」などの意味を持つようですが、ITの世界においては、「複数の実行中のプログラムなどが互いに他のプログラムの結果待ちとなり、待機状態に入ったまま動かなくなる現象を指す」ようです(e-word参照)。

データベースの世界においては、一つのトランザクションで複数のデータにロックをかける際に、そのロックをかける順番によっては、二つのトランザクションでお互いのロックの解放(アンロック)を待つ状態になってしまう、いわゆるお見合い状態になり、後続の処理が動かないことになってしまうことがあります。

言葉では分り難いので、図にしてみました。

先ずは正常時の動きです。二つのトランザクションが同じテーブルに対して更新処理をしようとします。この時、先にロックしたトランザクションの処理が終わるまで、もう一つが処理を待ち、アンロックされたタイミングで処理を開始することで、データ処理は正常に終了することができます。

複数トランザクションの処理(正常時)

次にデッドロック(異常時)の動きですが、これは二つのトランザクションが別々のテーブルのデータを先にロックし、次の処理でそれぞれのトランザクションがそのロックされたデータを処理する流れになっています。この場合、それぞれのデータが解放されるのを待って、解放され次第処理を進めるのですが、お互い解放しないので、次の処理へ進めない状態になってしまいます...

デッドロックの状態

デッドロック発生時の対処法は?

このデッドロックが発生してしまうとトランザクションが止まってしまうので、データベース(DBMS)はデッドロックを検出し、一方のトランザクションの処理を中止し、ロールバック(データを前に戻す)することで復旧させます。

また、デッドロックが発生しないようにするためにも、複数のトランザクションにおいて、データの呼び出し順序を同じにすると効果があるようですね!

2相ロックとは?

複数のテーブルにロックをかける際、ロックのかけ方によっては、トランザクションの一貫性や独立性が保証されないことがあります。

そのため、2相ロック方式という考え方があります。

2相ロック方式は、全てのトランザクションにおいて、読み書きに必要な全てのロックが完了するまではアンロック(解除)を行わないというものです。

ロックをする時にはロックし続け(これを単調増加と呼ぶ)、アンロックする時は外した状態にし続ける(これを単調減少と呼ぶ)仕組みです。

この方式によってデータベース上の矛盾は起きにくくなるのですが、それでも絶対起こらないとは限りません。難しいですね...

まとめ

今回はデータベースに欠かせないトランザクション管理を行う際に出てくる各種ロックに関して解説しました。

デッドロックに関しては、 タスクの状態遷移における資源の待ち状態など、データベース以外の分野でも出てくることがあります。デッドロックが発生するとシステムの処理が進まないので、デッドロックは早期検出も重要なポイントなんですね。

それにしてもデッドロック恐いですね...人生においても、お互い一歩も引かない状態でお見合いし続けている状況ってありますね...(笑)

以上です!

※排他制御は以下の記事で詳しく解説してます。

 

-データベース