ロック / デッドロック / トランザクションの定義


ロック

排他制御は、前項でも説明したように、データをロックすることにより実現することができます。

また、データベースへのアクセスのパフォーマンスなどの観点から、排他制御の方法にはいくつかの方法があります。
排他制御の形態は、ロックの形態に左右され、ここでは、概念的な話になりますが、ロックの様々な形態について説明します。

ロックの形態は、そのロックの程度から分類すると共有ロックと排他ロックの 2 種類 (2 モード) があります。

これはトランザクションの ACID 特性の一つである隔離性 (ISOLATION) のレベルにかかわってくるものです。

共有ロック

トランザクションが SELECT 文によりデータを参照するものであるときのロックのモードです。

共有ロックによりデータがロックされている間は、他のトランザクションからデータを参照することはできますが、データの変更はできません。

排他ロック

トランザクションが INSERT 文、UPDATE 文及び DELETE 文であるときのロックのモードです。

排他ロックされている間、他のトランザクションからデータの参照も変更もできません。

また、ロックするデータの範囲をロックの粒度またはロックレベルといいます。

ロックするデータの範囲を、テーブル全体にするのか、行全体なのか、特定の列なのかを示すもので、同時実効性を高めるには、ロックの粒度は小さいほうが望ましいと言えますが、トランザクションが衝突する可能性が高くなるという欠点があります。

逆に粒度が大きい場合は、トランザクションの衝突の可能性は減りますが、同時実効性が低くなってしまいます。

実行しようとするトランザクションが共有ロックであるのか、排他ロックであるのかは、後に述べる SET TRANSACTION 文で指定することができます。

ロックの粒度も同じ構文で指定することができます。

デッド ロック

データのロックにより発生するものに、デッドロックというものがあります。

これは複数のトランザクションが互いの処理に必要なデータをロックし合っているために、処理不能の状態になってしまうことです。

トランザクション A がトランザクション B がロックしているデータ B を参照し、トランザクション B はトランザクション A がロックしているデータ A を参照しようとした場合、双方のトランザクションがデータロック解除待ち状態になってしまいます。

デッドロックを防ぐには、トランザクションを設計するときに、アクセスするデータの順を決めておくことがよいとされています。

つまり、トランザクション A ではデータ A の次にデータ B を処理するという風にするならば、トランザクション B でもデータ A の次にデータ B を処理するという風にすればよいというわけです。

デッドロックを防ぐ方法として、二相ロック方式という考え方もあります。

これは、あるトランザクションが処理対象としているデータのすべてを、トランザクションの開始時にまとめてロックしてしまい、トランザクションが終了する直前にそのロックをすべて開放するというものです。

二相ロック方式によりロックされたデータは、トランザクションで処理中でないデータに対しても、他のトランザクションはデータの変更などの処理をすることができなくなります。

トランザクションの定義

ここでいうトランザクションの定義とは、SET TRANSACTION 文を用いて、次の項目について設定を行うということです。

  • アクセスモードトランザクションが読み取り専用であるのか、読み取り / 書き込み可能なのかを指定する。初期値は READ WRITE(読み取り / 書き込み可能) です。
  • 隔離レベル ACID 特性の隔離性 (ISOLATION) のレベルを指定する。初期値は READ COMMITTED です。
  • 診断域 SQL 文の処理結果に関する情報を格納する領域を指定します。トランザクションは、暗黙的に開始された場合でも、明示的に開始された場合でも、SET TRANSACTION 文により上記 3つの項目について設定を行わなかった場合は、それぞれの初期値で実行さます。

SQL92 における SET TRANSACTION の基本構文は次の通りです。

SET TRANSACTION
 [ {READ ONLY | READ WRITE} ]
 [ ISOLATION LEVEL
   {READ UNCOMMITTED | READ COMMITTED |
    REPEATABLE READ | SERIALIZABLE}]
 [ DIAGNOSTICS SIZE 行数 ] ;

この基本構文のそれぞれの項目について説明します。

アクセスモードは READ ONLY 句または READ WRITE 句で指定します。

  • READ ONLY    読み取り専用
  • READ WRITE    読み書き両用(初期値)

READ ONLY 句を指定すると、そのトランザクションは読み取り専用モードになります。
このトランザクションで SELECT 文を実行すると、トランザクション開始前の状態のデータを参照することができます。
このトランザクション実行中に他のトランザクションでデータに変更が加えられても、その前の状態のデータを参照できるということです。

READ WRITE 句を指定すると、そのトランザクションは読み書き両用モードになります。
このトランザクションでは INSERT 文、UPDATE 文及び DELETE 文を使用してデータを変更することができます。
このトランザクションで SELECT 文を実行すると、他のトランザクションによるデータの変更も参照することができます。

隔離レベルは、READ UNCOMMITTED 句、READ COMMITTED 句、REPEATABLE READ 句または SERIALIZABLE 句で指定します。

READ UNCOMMITTED 共有ロック、排他ロックも使用されない、最も隔離性の緩やかなレベルである。ダーティリード、ノンリピータブルリード、ファントムインサートが発生する可能性がある。
READ COMMITTED トランザクションが開始されて、まだコミットされていない場合には、他のトランザクションからはデータを参照することができないレベルである。従って、 ダーティリードは発生しない。しかし、他のトランザクションによりデータの変更は可能なため、ノンリピータブルリード、ファントムインサートが発生する可 能性がある。
REPEATABLE READ トランザクションが完了するまで共有ロックが継続されるので、一度読み取ったデータが他のトランザクションによって変更されることはない。ファントムインサートが発生する可能性がある。
SERIALIZABLE 他のトランザクションからは完全に隔離したレベルで、他のトランザクションの影響を一切うけない。トランザクション実行中に、他のトランザクションがアク セスしても、そのトランザクションは待機状態になり、前のトランザクションが終了するまでデータにアクセスすることができない。

隔離レベルについては表に示す通りだが、複数のトランザクションが同じデータにアクセスすることにより、発生する問題が 4つあります。

それは、ロストアップデート、ダーティリード、ノンリピータブルリード及びファントムインサートという現象です。

ロストアップデート あるトランザクションで変更した値が、他のトランザクションにより別の値に変更されてしまうこと。
ダーティリード 他のトランザクションがデータの変更を取り消したにも関わらず、取り消し前にそのデータを読み出してしまうこと。
ノンリピータブルリード あるトランザクションにおいて、同じ行を 2 回読み込んだときに、1 回目と 2 回目の読み込みの間に、他のトランザクションが行の値の変更を行ったために、1 回目と 2 回目で読み込んだデータの値が異なってしまうこと。
ファントムインサート あるトランザクションにおいて、WHERE 句等による同じ条件により行を読み込んだときに、その途中に他のトランザクションがその条件に影響を与える行の挿入を行ったために、先に読み込んだ行と後に読み込んだ行が異なってしまうこと。

それぞれ、例をあげて説明します。
ここでは、テーブル名 TableA、TableA の列 ColA を例に説明する。

  • TransactionA と TransactionB が、ColA=10 であることを確認した後に、TransactionA が ColA の値を 10 から 8 に変更する。
    しかし、その直後に TransactionB が ColA の値を 15 に変更してしまうと、TransactionA の行ったデータの更新はデータに反映されなかったことになる。このような現象をロストアップデートという。
  • TransactionA が TableA の ColA の値を最初に確認した後に、UPDATE 文で ColA の値を 10 から 15 に変更する。
    この後で、TransactionB は ColA の値を読み取る。
    その後、TransactionA が変更処理を ROLLBACK 文で変更してしまったとき、TransactionB の読み取った値は、確定されなかったものになってしまう。
    このような現象をダーティリードという。
  • TransactionB が ColA の値を読み取った後で、TransactionA が ColA の値を 10 から 15 に変更し、その後、TransactionB が再び ColA の値を読み取ると、TransactionB は同じ行に対して 1 回目と 2 回目で異なる値を読み取ってしまうことになる。
    このような現象をノンリピータブルリードという。
  • TransactionB が TableA の ColA 列の値の平均を読み取った後で、TransactionA が行を追加し、その後、TransactionB が再び TableA の ColA 列の値の平均を読み取ろうとすると、TransactionB は以前と異なる値を読み取ってしまうことになる。
    このような現象をファントムインサートという。

最後に、トランザクションの隔離レベルと隔離性に関する問題の関係をまとめました。
表中の×は起こりうることを意味します。

隔離レベル ロストアップデート ダーティリード ノンリピータブルリード ファントムインサート
READ UNCOMMITTED × × ×
READ COMMITTED × ×
REPEATABLE READ ×
SERIALIZE

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です


8 − = 2

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>