• 検索結果がありません。

STEP 3. トランザクションの分離と Isolation Level

3.6 ファントム読み取り(Phantom Read)

ファントム読み取り

ファントム読み取り(Phantom Read)は、一度読み取ったデータが、ほかのトランザクションに よって削除(DELETE)されたり、同じ値が追加(INSERT)されたりするという矛盾です。Phantom は、「幻」や「幻影」という意味で、読み取ったはずのデータが消えていたり、増えていたりする ことから、ファントム読み取りと呼ばれています。

ファントム読み取りでの、「読み取ったデータが DELETE される」という矛盾は、Repeatable

Read レベルで回避できるのですが、「同じ値が INSERT される」という矛盾を回避することは

できません。読み取り時に存在しないデータ(これから INSERT されるデータ)には、ロックの かけようがないからです。これは次のような状況です。

Let's Try

それでは、これを試してみましょう。

1. まずは、ツールバーの[新しいクエリ]から、2つの接続を作ります。

2. 1つ目の接続から、SELECT ステートメントを実行して、「t1」の中から「b='AAA'」のデー タを検索します(COMMIT TRAN を省略して、トランザクション中とします)。

USE sampleDB BEGIN TRAN

SELECT * FROM t1 WHERE b = 'AAA'

3. 次に、2つ目の接続から、データを 1件追加します。

USE sampleDB BEGIN TRAN

INSERT INTO t1 VALUES(5, 'AAA') COMMIT TRAN

SELECT * FROM t1

4. 次に、1つ目の接続から、もう一度「b='AAA'」のデータを検索します。

SELECT * FROM t1 WHERE b = 'AAA'

このように、トランザクション中に読み取ったデータが、1 回目と 2 回目で異なる結果にな る現象(増えたり、減ったりする現象)がファントム読み取りです。

1回目の結果

1回目には存在しな かったデータ

(ファントム値)

矛盾

ファントム読み取りの回避 ~Serializable レベル~

ファントム読み取りを回避できるのが、Serializable レベルです。これは、内部的には次のように ロックの動作を変更することで実現しています。

1. 共有ロックをトランザクションが完了するまで保持

2. 共有ロックを該当データだけでなく、その周囲のデータもロックする(範囲ロックをかけ る)

1 つ目は Repeatatble Read レベルと同じですが、2 つ目の動作が Serializableレベルだけの特 徴です。Serializableレベルでは、同じデータが INSERT されるのを防ぐために、範囲(Range)

ロックを利用しています。

Let's Try

それでは、これを試してみましょう。

1. 前の手順と同じように、ツールバーの[新しいクエリ]から、2つの接続を作ります。

2. 1つ目の接続から、SELECT ステートメントを実行して、「t1」の中から「b='AAA'」のデー タを検索します(COMMIT TRAN を省略して、トランザクション中とします)。このときに、

分離レベルを「Serializable」へ指定します。

USE sampleDB BEGIN TRAN

SELECT * FROM t1 WITH(Serializable) WHERE b = 'AAA'

3. 次に、2つ目の接続から、データを 1件追加します。

USE sampleDB BEGIN TRAN

INSERT INTO t1 VALUES(6, 'AAA') COMMIT TRAN

Serializable を指定

今度は、ロック待ちになり、INSERT ステートメントを実行することはできません。このよ

うに、Serializable レベルを利用すると、読み取り中のデータに対して、同じデータが追加さ

れたり、削除されたりすることを防げるようになります。

Serializable の問題点 ~デッドロックの多発~

Repeatable Read レベルのところで説明したデッドロックが多発する問題は、Serizalizable レベ ルにも当てはまります。したがって、実際に Serizalizable レベルを利用する場面は、ほとんどあ りません。

Note: TransactionScope と COM+コンポーネントのデフォルトは Serializable

前述の Note で記述したように TransactionScope オブジェクトと COM+ コンポーネントのデフォルトの分離レベ ルは、Serizalizable であることに注意する必要があります。

ファントム読み取りを回避するには、アプリケーション側でファントムデータのチェックをしたり、

重複値の INSERT の場合は UNIQUE 制約を利用したり、運用でカバーしたりするようにします。

また、「更新ロック」や、後述の「楽観的同時実行制御」が利用できる場合は、そちらを利用しま す。

Serializable を指定

ロック待ち状態