次の方法で共有


メモリ最適化テーブルでのトランザクション

ディスク ベース テーブルでの行のバージョン管理 (SNAPSHOT 分離または READ_COMMITTED_SNAPSHOT を使用) では、オプティミスティック コンカレンシーの形式を使用します。 リーダーとライターは相互にブロックしません。 メモリ最適化テーブルでは、ライターはライターをブロックしません。 ディスク ベース テーブルの行のバージョン管理では、あるトランザクションが行をロックすると、この行を更新しようとする同時実行トランザクションがブロックされます。 メモリ最適化テーブルの場合は、ロックは生じません。 その代わり、2 つのトランザクションで同じ行を更新しようとすると、書き込みと書き込みの競合 (エラー 41302) が発生することになります。

ディスク ベースのテーブルとは異なり、メモリ最適化テーブルでは、より高い分離レベルである REPEATABLE READ と SERIALIZABLE を使用してオプティミスティック コンカレンシー制御を行うことができます。 分離レベルを強制するためにロックが使用されることはありません。 その代わり、トランザクションの終了時に、REPEATABLE READ または SERIALIZABLE の仮定を確認する検証が適用されます。 仮定に違反している場合、トランザクションは終了します。 詳細については、「 Transaction Isolation Levels」をご覧ください。

メモリ最適化テーブルの重要なトランザクション セマンティクスは次のとおりです。

  • 複数バージョン管理

  • スナップショットベースのトランザクション分離

  • Optimistic

  • 競合検出

これらのセマンティクスのそれぞれについて、以下のセクションで説明します。

メモリ最適化テーブルでの複数バージョン管理

メモリ最適化テーブルの行は、バージョンが異なる場合があります。 同時実行トランザクションでは、同じ行の異なるバージョンにアクセスする可能性があります。

メモリ最適化テーブルのデータは、バージョンに基づいています。 いずれの行についても、さまざまに異なる時点で有効な異なる行バージョンがあります。 ディスク ベース テーブルでは、READ_COMMITTED_SNAPSHOT または ALLOW_SNAPSHOT_ISOLATION が ON に設定されている場合に、異なる行バージョンが保持されます。 メモリ最適化テーブルでは、READ_COMMITTED_SNAPSHOT および ALLOW_SNAPSHOT_ISOLATION が OFF に設定されている場合でも、異なる行バージョンが保持されます。 メモリ最適化テーブルの行バージョンは、tempdb では保持されません。 代わりに、行バージョンは、メモリ内に行を格納するメモリ最適化データ構造の一部として、インラインに保持されます。

メモリ最適化テーブルのスナップショットベースのトランザクション分離

1 つのトランザクションのすべての操作では、メモリ最適化テーブルについてトランザクション上の一貫性のある同じスナップショットを使用します。 メモリ最適化テーブルのトランザクション分離はすべてスナップショットベースです。 たとえば、SERIALIZABLE 分離レベルを使用してメモリ最適化されるテーブルにアクセスするトランザクションでは、トランザクション上の一貫性のある同じスナップショットに対するすべての操作を実行します。

メモリ最適化テーブルにアクセスするトランザクションでは、この行のバージョン管理を使用して、テーブルの行についてトランザクション上の一貫性のあるスナップショットを取得します。 トランザクションの各ステートメントで読み取るデータは、トランザクション上の一貫性のあるバージョンのデータとなります。これは、トランザクションの開始時に存在していたデータです。 したがって、同時実行トランザクションで行われる変更は、現在のトランザクションのステートメントからは認識されません。

メモリ最適化テーブルのオプティミスティック コンカレンシー

競合と失敗はまれであり、メモリ最適化テーブルに対するトランザクションでは、同時実行トランザクションに競合はなく、操作は成功すると仮定します。 トランザクションでは、トランザクション分離を保証するためのロックまたはラッチをメモリ最適化テーブルに対して使用しません。 ライターはリーダーをブロックしません。 ライターはライターをブロックしません。 その代わり、トランザクションは、他のトランザクションとの競合がないという (オプティミスティックな) 仮定の下で実行されます。 ロックおよびラッチを使用せず、他のトランザクションが同じ行を処理し終わるまで待機しないことで、パフォーマンスが向上します。

これに加えて、トランザクション (TxA) がコミット処理中の他のトランザクション (TxB) で挿入または変更された行を読み取る場合、コミットの発生を待機する代わりに他のトランザクションがコミットされると仮定します。 この場合、トランザクション TxA は、トランザクション TxB に対するコミット依存関係を適用します。

競合検出、検証、コミットの依存関係の確認

SQL Serverは、同時実行トランザクション間の競合と分離レベル違反を検出し、競合するトランザクションの 1 つを運命付けます。 このトランザクションは再試行する必要があります。 (詳細については、「 Memory-Optimized テーブルでのトランザクションの再試行ロジックのガイドライン」を参照してください)。

システムでは、競合もトランザクション分離の違反もないとオプティミスティックに仮定します。 データベースの一貫性が損なわれる可能性がある競合や、トランザクション分離に違反する可能性がある競合が発生した場合は、それらの競合が検出されて、そのトランザクションは終了します。

競合が検出されると、トランザクションは終了し、クライアントは再試行する必要があります。

次の表は、メモリ最適化テーブルにアクセスするトランザクションのエラー状態をまとめたものです。

メモリ最適化テーブルにアクセスするトランザクションのエラー状態。

エラー シナリオ
書き込みの競合。 トランザクションの開始以降に更新されたレコードを更新しようとしています。 同時実行トランザクションによって更新または削除された行を更新または削除します。
REPEATABLE READ の検証の失敗。 トランザクションで読み取った行が、トランザクションの開始以降に変更 (更新または削除) されています。 REPEATABLE READ および SERIALIZABLE トランザクション分離レベルを使用する際には通常、REPEATABLE READ の検証が行われます。
SERIALIZABLE の検証の失敗。 新しい (ファントム) 行が、トランザクションの開始以降にトランザクションのスキャンの範囲の 1 つに挿入されています。 トランザクションの開始前に行がデータベースにコミットされている場合、トランザクションではその行を認識できます。 SERIALIZABLE 分離を使用し、PRIMARY キー制約を検証する際には通常、SERIALIZABLE の検証が行われます。
コミットの依存関係の失敗。 トランザクションで他のトランザクションに対する依存関係を適用しましたが、この表に記載されたエラーのいずれか、メモリ不足の状態、またはトランザクション ログへのコミットの失敗が原因で、コミットが失敗しました。 このエラーは、読み取り/書き込みトランザクションおよび読み取り専用トランザクションの両方で発生する可能性があります。

トランザクションの有効期間

前の表に示すエラーは、トランザクションのさまざまなポイントで発生する可能性があります。 次の図は、メモリ最適化テーブルにアクセスするトランザクションのフェーズを示しています。

トランザクションの有効期間。 メモリ最適化テーブルにアクセスするトランザクションの有効期間。

通常の処理

このフェーズでは、ユーザーが発行した Transact-SQL ステートメントが実行されます。 行がテーブルから読み取られ、新しい行バージョンがデータベースに書き込まれます。 トランザクションは、他のすべての同時実行トランザクションから分離されます。 トランザクションでは、トランザクションの開始時点で存在するメモリ最適化テーブルのスナップショットを使用します。

トランザクションのこのフェーズにおけるテーブルへの書き込みは、他のトランザクションではまだ確認できません。ただし、1 つの例外として、行の更新と削除は、書き込みの競合を検出するために、他のトランザクションでの更新操作と削除操作で確認できます。

トランザクションの論理的な開始以降に行が更新または削除されたことを更新操作や削除操作で確認すると、その操作はエラー 41302 で失敗します。 エラー 41302 のメッセージは、"現在のトランザクションが、トランザクションが開始してから更新されたテーブル X のレコードを更新しようとしました。 トランザクションは中止されました。" というものです。

このエラーが発生すると、(XACT_ABORT が OFF の場合でも) トランザクションが破棄されます。つまり、トランザクションはユーザー セッションの終了時にロールバックされます。 失敗したトランザクションはコミットできません。ログに書き込まず、メモリ最適化テーブルにアクセスしない読み取り操作のみがサポートされます。

コミット依存関係

通常の処理中、トランザクションでは、検証またはコミット フェーズであるがまだコミットされていない、他のトランザクションによって書き込まれた行を読み取ることができます。 トランザクションの論理的終了時刻が検証フェーズの開始時に割り当てられているため、行が表示されます。

コミットされていない行をトランザクションで読み取る場合は、そのトランザクションに対するコミット依存関係を使用します。 これには 2 つの主要な関連事項があります。

  • トランザクションは、依存先のトランザクションがコミットされるまでコミットできません。 つまり、依存関係がすべてクリアされるまで、コミット フェーズに入ることはできません。

  • また、結果セットは、依存関係がすべてクリアされるまで、クライアントに返されません。 したがって、クライアントはコミットされていないデータを確認できません。

従属トランザクションのいずれかがコミットに失敗した場合は、コミット依存関係のエラーが発生します。 つまり、トランザクションはエラー 41301 ("現在のトランザクションが依存している前のトランザクションが中止されたため、現在のトランザクションはコミットできません。") によりコメントされません。

検証フェーズ

検証フェーズでは、トランザクションの論理的開始から論理的終了までの期間について、システムは要求されたトランザクション分離レベルに必要な仮定が true であったことを検証します。

検証フェーズの開始時に、トランザクションには論理的終了時刻が割り当てられます。 データベースに書き込まれた行バージョンは、論理的終了時刻の時点で他のトランザクションから確認できるようになります。 詳細については、「依存関係の コミット」を参照してください。

REPEATABLE READ の検証

トランザクションの分離レベルが REPEATABLE READ または SERIALIZABLE の場合、または REPEATABLE READ または SERIALIZABLE 分離の下でテーブルにアクセスする場合 (詳細については、「 トランザクション分離レベルでの個々の操作の分離」のセクションを参照)、システムは読み取りが反復可能であることを検証します。 つまり、トランザクションによって読み取られる行バージョンが、トランザクションの論理的終了時刻の時点で依然として有効な行バージョンであることを検証します。

更新または変更された行がある場合、トランザクションはエラー 41305 ("現在のトランザクションは、REPEATABLE READ の検証の失敗が原因でコミットされませんでした。") によりコミットされません。

このエラーは、挿入操作、更新操作、または削除操作の後、およびトランザクションのコミット前にテーブルが削除された場合にも発生する可能性があります。 このことは、ネイティブ コンパイル ストアド プロシージャでの挿入操作、更新操作、または削除操作にのみ当てはまります。 解釈された Transact-SQL を介して実行されるこのような書き込み操作では、DROP TABLE ステートメントがブロックされ、トランザクションがコミットされるまで待機します。

SERIALIZABLE の検証

SERIALIZABLE の検証は、次の 2 つの場合に発生します。

  • トランザクションの分離レベルが SERIALIZABLE であるか、テーブルが SERIALIZABLE 分離の下でアクセスされる場合。

  • PRIMARY KEY 制約に対して作成されたインデックスなど、一意のインデックスに行が挿入された場合。 システムは、同じキーを持つ行が同時に挿入されていないことを検証します。

システムは、ファントム行がデータベースに書き込まれていないことを検証します。 トランザクションで実行される読み取り操作が評価され、その読み取り操作のスキャン範囲に新しい行が挿入されていないことが判定されます。

一意のインデックスにキーを挿入する場合は、暗黙の読み取り操作が行われ、そのキーが重複していないことが判定されます。 一意のインデックスに対する SERIALIZABLE の検証では、2 つのトランザクションで同時に同じキーを挿入した場合、これらのインデックスで重複が不可能であることを確認します。

ファントム行が検出された場合、トランザクションはエラー 41325 ("現在のトランザクションは、REPEATABLE READ の検証の失敗が原因でコミットされませんでした。") によりコミットされません。

コミット処理

検証が成功し、トランザクション依存関係がすべてクリアされると、トランザクションはコミット処理フェーズに入ります。 このフェーズでは、持続性のあるテーブルへの変更はログに書き込まれ、そのログはディスクに書き込まれて、持続性が確保されます。 トランザクションのログ レコードがディスクに書き込まれると、制御はクライアントに返されます。

このトランザクションの依存関係はすべてクリアされ、このトランザクションのコミットを待機していたすべてのトランザクションを処理できるようになります。

制限事項

  • データベース間トランザクションは、メモリ最適化テーブルではサポートされていません。 メモリ最適化テーブルにアクセスする各トランザクションは、複数のデータベースにアクセスできません。ただし、tempdb への読み取り/書き込みアクセスとシステム データベース マスターへの読み取り専用アクセスは例外です。

  • 分散トランザクションは、メモリ最適化テーブルではサポートされません。 BEGIN DISTRIBUTED TRANSACTION で開始される分散トランザクションは、メモリ最適化テーブルにアクセスできません。

  • メモリ最適化テーブルは、ロックをサポートしません。 ロック ヒント (TABLOCK、XLOCK、ROWLOCK など) による明示的なロックは、メモリ最適化テーブルではサポートされません。

参照

メモリ最適化テーブルを対象にするトランザクションについて