行のバージョン管理に基づく分離レベルについて

行のバージョン管理は、次の目的で使用される SQL Server の一般的なフレームワークです。

  • トリガに inserted テーブルと deleted テーブルを構築します。トリガにより変更された行はすべて、バージョン化されます。これには、トリガによりデータが変更された行だけでなく、トリガを起動したステートメントにより変更された行も含まれます。

  • 複数のアクティブな結果セット (MARS) をサポートします。アクティブな結果セットが存在するときに、MARS セッションでデータ変更ステートメント (INSERT、UPDATE、DELETE など) が実行された場合、その変更ステートメントの影響を受けた行はバージョン化されます。

  • ONLINE オプションを指定するインデックス操作をサポートします。

  • 行のバージョン管理に基づく次のトランザクション分離レベルをサポートします。

    • 行のバージョン管理を使用してステートメントレベルの読み取りの一貫性を保証する新しい READ COMMITTED 分離レベルの実装。

    • トランザクションレベルの読み取りの一貫性を保証する新しいスナップショット分離レベル。

tempdb データベースには、バージョン ストア用の十分なディスク領域が必要です。tempdb がいっぱいになると、更新操作では、操作を完了するためにバージョンの生成を停止して処理を続行しますが、読み取り操作は失敗することがあります。これは、必要な特定の行のバージョンが存在しないためです。特定の行のバージョンが存在しないことにより、トリガ、MARS、オンラインのインデックス構築などの操作が影響を受けます。詳細については、「行のバージョン管理用リソースの使用状況」を参照してください。

READ COMMITTED トランザクションとスナップショット トランザクションで行のバージョン管理を使用するには、次の 2 つの手順を実行します。

  1. READ_COMMITTED_SNAPSHOT データベース オプションと ALLOW_SNAPSHOT_ISOLATION データベース オプションのいずれかまたは両方を ON に設定します。

  2. 次の説明に従って、アプリケーションで適切なトランザクション分離レベルを設定します。

    • READ_COMMITTED_SNAPSHOT データベース オプションを ON に設定すると、READ COMMITTED 分離レベルを設定するトランザクションで行のバージョン管理が使用されます。

    • ALLOW_SNAPSHOT_ISOLATION データベース オプションを ON に設定すると、トランザクションでスナップショット分離レベルを設定できます。

READ_COMMITTED_SNAPSHOT データベース オプションまたは ALLOW_SNAPSHOT_ISOLATION データベース オプションのいずれかを ON に設定すると、SQL Server データベース エンジンにより、行のバージョン管理を使用してデータを操作する各トランザクションにトランザクション シーケンス番号 (XSN) が割り当てられます。トランザクションは、BEGIN TRANSACTION ステートメントが実行されたときに開始されます。ただし、トランザクション シーケンス番号が始まるのは、BEGIN TRANSACTION ステートメントの後に実行される最初の読み取り操作または書き込み操作からです。トランザクション シーケンス番号は、トランザクションに割り当てられるたびに 1 ずつ増加します。

READ_COMMITTED_SNAPSHOT データベース オプションまたは ALLOW_SNAPSHOT_ISOLATION データベース オプションのいずれかを ON に設定すると、データベースで実行されるすべてのデータ変更の論理コピー (バージョン) が保持されます。特定のトランザクションで行が変更されるたびに、データベース エンジンのインスタンスにより、行の以前にコミットされたイメージのバージョンが tempdb に格納されます。各バージョンには、その変更を行ったトランザクションのトランザクション シーケンス番号が付きます。変更された行のバージョンは、リンク リストを使用して連結されます。最新の行の値は、常に現在のデータベースに格納され、tempdb に格納されているバージョン管理された行に連結されます。

注意

ラージ オブジェクト (LOB) の変更については、変更された部分のみが tempdb のバージョン ストアにコピーされます。

バージョンストアに格納されているバージョンは、行のバージョン管理に基づく分離レベルで実行されるトランザクションで必要な限り保持されます。データベース エンジンにより、必要なトランザクション シーケンス番号の中で最も小さい番号が追跡され、それよりもトランザクション シーケンス番号が小さい行のバージョンは定期的にすべて削除されます。

両方のデータベース オプションを OFF に設定すると、トリガまたは MARS セッションで変更された行、あるいは ONLINE インデックス操作で読み取られた行のみがバージョン管理されます。これらの行のバージョンは、必要ではなくなった時点で解放されます。また、定期的に実行されるバックグラウンドのスレッドにより、古い行のバージョンが削除されます。

注意

トランザクションの実行時間が短い場合、変更された行のバージョンは、tempdb データベースのディスク ファイルに書き込まれずにバッファ プールにキャッシュされる場合があります。バージョン管理された行が必要とされる時間が短い場合、その行のバージョンは単純にバッファ プールから削除されるので、I/O のオーバーヘッドが発生しない場合もあります。

データ読み取り時の動作

行のバージョン管理に基づく分離レベルで実行されているトランザクションによりデータが読み取られるとき、読み取り操作では、読み取るデータに対して共有 (S) ロックが獲得されないので、データを変更しているトランザクションはブロックされません。また、リソースのロックによるオーバーヘッドは、獲得されるロックの数が少ないほど小さくなります。行のバージョン管理を使用する READ COMMITTED 分離とスナップショット分離は、バージョン管理されたデータの読み取りの一貫性をステートメントレベルまたはトランザクションレベルで保証するようにデザインされています。

行のバージョン管理に基づく分離レベルで実行されているトランザクションを含むすべてのクエリは、コンパイルおよび実行中に Sch-S (スキーマ安定度) ロックを獲得します。このため、同時実行トランザクションがテーブルの Sch-M (スキーマ修正) ロックを保持している場合、クエリはブロックされます。たとえば、データ定義言語 (DDL) 操作では、テーブルのスキーマ情報を変更する前に Sch-M ロックを獲得します。行のバージョン管理に基づく分離レベルで実行されているトランザクションを含むクエリ トランザクションは、Sch-S ロックを獲得しようとするとブロックされます。逆に、Sch-S ロックを保持しているクエリは、Sch-M ロックを獲得しようとする同時実行トランザクションをブロックします。ロック動作の詳細については、「ロックの互換性 (データベース エンジン)」を参照してください。

スナップショット分離レベルを使用するトランザクションが開始されると、データベース エンジンのインスタンスにより、現在アクティブなトランザクションがすべて記録されます。スナップショット トランザクションでバージョン チェーンを持つ行が読み取られると、データベース エンジンによりチェーンが追跡され、次のトランザクション シーケンス番号を持つ行が取得されます。

  • 行を読み取っているスナップショット トランザクションのシーケンス番号に最も近く、それよりも小さいトランザクション シーケンス番号。

  • スナップショット トランザクションが開始されたときのアクティブなトランザクションの一覧にないトランザクション シーケンス番号。

スナップショット トランザクションで実行される読み取り操作では、スナップショット トランザクションが開始されたときにコミットされた各行の最後のバージョンが取得されます。これにより、トランザクション内で一貫性を持つ、トランザクションが開始されたときのデータのスナップショットが提供されます。

行のバージョン管理を使用する READ COMMITTED トランザクションも、ほぼ同じように動作します。ただし、READ COMMITTED トランザクションでは、行のバージョンを選択するときにトランザクション自体のトランザクション シーケンス番号が使用されないという点が異なります。ステートメントが開始されるたびに、READ COMMITTED トランザクションでは、このデータベース エンジンのインスタンスに対して生成された最新のトランザクション シーケンス番号が読み取られます。これは、そのステートメントに適した行のバージョンを選択するために使用されるトランザクション シーケンス番号です。このトランザクション シーケンス番号により、READ COMMITTED トランザクションでは、各ステートメントが開始されたときのデータのスナップショットを参照できるようになります。

注意

行のバージョン管理を使用する READ COMMITTED トランザクションが、トランザクション全体で一貫性のあるデータのビューをステートメント レベルで提供しても、この種類のトランザクションにより生成またはアクセスされる行バージョンは、トランザクションが完了するまで保持されます。

データ変更時の動作

行のバージョン管理を使用する READ COMMITTED トランザクションでは、更新する行を選択するときにブロッキング スキャンが使用されます。ブロッキング スキャンでは、データ値を読み取るときにデータ行の更新 (U) ロックが獲得されます。この動作は、行のバージョン管理を使用しない READ COMMITTED トランザクションでも発生します。データ行が更新基準を満たしていない場合は、その行の更新ロックが解放され、次の行がロックおよびスキャンされます。

スナップショット分離レベルで実行されているトランザクションでは、制約を設定するためだけに変更を実行する前に、データのロックを獲得することによって、データ変更にオプティミスティック同時実行制御が使用されます。それ以外の場合、データの変更が確定するまで、そのデータのロックは獲得されません。データ行が更新基準を満たしている場合、スナップショット トランザクションにより、そのスナップショット トランザクションの開始後にコミットされた同時実行トランザクションでそのデータ行が変更されていないかどうかが確認されます。データ行がスナップショット トランザクションの外部で変更された場合は、更新の競合が発生し、そのスナップショット トランザクションは終了されます。更新の競合はデータベース エンジンによって処理されるので、更新の競合が検出されないようにする方法はありません。

注意

スナップショット分離レベルで実行されている更新操作は、スナップショット トランザクションにより次のアイテムへのアクセスが行われたときに、内部的に READ COMMITTED 分離レベルで実行されます。

FOREIGN KEY 制約が適用されたテーブル。

別のテーブルの FOREIGN KEY 制約で参照されるテーブル。

複数のテーブルを参照するインデックス付きビュー。

ただしこのような状況でも、更新操作では、データが別のトランザクションにより変更されていないかどうかが引き続き確認されます。データが別のトランザクションで変更されている場合は、更新の競合が発生し、スナップショット トランザクションは終了します。

動作のまとめ

次の表に、行のバージョン管理を使用するスナップショット分離レベルと READ COMMITTED 分離レベルの違いを要約します。

特性

行のバージョン管理を使用する READ COMMITTED 分離レベル

スナップショット分離レベル

必要なサポートを有効にするために ON に設定されている必要があるデータベース オプション

READ_COMMITTED_SNAPSHOT

ALLOW_SNAPSHOT_ISOLATION

セッションが特定の種類の行のバージョン管理を要求する方法

既定の READ COMMITTED 分離レベルを使用するか、または SET TRANSACTION ISOLATION LEVEL ステートメントを実行して READ COMMITTED 分離レベルを指定します。この操作は、トランザクションの開始後に実行できます。

トランザクションの開始前に、SET TRANSACTION ISOLATION LEVEL を実行してスナップショット分離レベルを指定するように要求します。

ステートメントにより読み取られるデータのバージョン

各ステートメントの開始前にコミットされたすべてのデータ。

各トランザクションの開始前にコミットされたすべてのデータ。

更新の処理方法

行のバージョンを実際のデータに戻して更新する行を選択し、選択したデータ行に対して更新ロックをかけます。また、変更する実際のデータ行の排他ロックを獲得します。更新の競合検出は行われません。

行バージョンを使用して更新する行を選択します。変更する行の実際のデータに対する排他ロックの獲得を試行します。このデータが別のトランザクションにより変更されている場合は、更新の競合が発生し、スナップショット トランザクションは終了します。

更新の競合検出

なし。

組み込みによるサポート。この機能は無効にできません。