TL; DR:次の質問に要約します:行を挿入するとき、新しい値の生成とクラスター化インデックス内の対応する行キーのロックの間に機会がありますか?外部オブザーバーはより新しいものを見ることができます並行トランザクションによって挿入された値?(SQL Serverで。)Identity
Identity
詳細バージョン
Identity
という名前の列を持つSQL ServerテーブルがありますCheckpointSequence
。これは、テーブルのクラスター化インデックスのキーです(これには、追加の非クラスター化インデックスもいくつかあります)。行は、いくつかの並行プロセスとスレッドによって(分離レベルで、なしで)テーブルに挿入されます。同時に、クラスター化インデックスから定期的に行を読み取るプロセスがあり、その列で並べ替えられます(分離レベルでも、オプションはオフになっています)。READ COMMITTED
IDENTITY_INSERT
CheckpointSequence
READ COMMITTED
READ COMMITTED SNAPSHOT
私は現在、読み取りプロセスがチェックポイントを「スキップ」できないという事実に依存しています。私の質問は、このプロパティに依存できますか?そうでない場合、それを実現するために何ができますか?
例:ID値が1、2、3、4、および5 の行を挿入する場合、値4の行を表示する前に、値5の行を参照してはなりません。テストは、ORDER BY CheckpointSequence
句(そしてWHERE CheckpointSequence > -1
確実句)、ブロック行4、行5が既にコミットされていても、読まれるべきであるが、まだコミットされていない時はいつでも。
少なくとも理論的には、この仮定を破る可能性のある競合状態がここにあると思います。残念ながら、上のドキュメントでIdentity
はIdentity
、複数の同時トランザクションのコンテキストでどのように機能するかについてはあまり言及されていません。「特定のトランザクションの新しい値はそれぞれ、テーブル上の他の同時トランザクションとは異なります。」(MSDN)
私の推論は、それはこのように何らかの形で動作する必要があります:
- トランザクションが(明示的または暗黙的に)開始されます。
- ID値(X)が生成されます。
- 対応する行ロックは、ID値に基づいてクラスター化インデックスで取得されます(ロックエスカレーションが開始されない限り、この場合、テーブル全体がロックされます)。
- 行が挿入されます。
- トランザクションがコミットされる(おそらくかなり長い時間後に)ので、ロックは再び削除されます。
ステップ2と3の間に、非常に小さなウィンドウがあると思います
- 同時セッションは次のID値(X + 1)を生成し、残りのすべてのステップを実行できます。
- したがって、その時点で正確に来ている読者が値X + 1を読み取ることができ、Xの値は失われます。
もちろん、これの可能性は非常に低いようです。しかし、まだ-それは起こる可能性があります。それともできますか?
(コンテキストに興味がある場合:これは、NEventStoreのSQL Persistence Engineの実装です。NEventStoreは、すべてのイベントが新しい昇順チェックポイントシーケンス番号を取得する追加専用のイベントストアを実装します。クライアントは、チェックポイント順にイベントストアからイベントを読み取りますすべての種類の計算を実行するため。チェックポイントXのイベントが処理されると、クライアントは「新しい」イベント、つまりチェックポイントX + 1以上のイベントのみを考慮します。したがって、イベントをスキップしないことが重要です。現在、Identity
ベースのチェックポイント実装がこの要件を満たしているかどうかを判断しようとしています。これらは、使用されている正確なSQLステートメントです。Schema、Writerのquery、読者の質問。)
私が正しいと上記の状況が発生する可能性がある場合、それらに対処する2つのオプションのみが表示されますが、どちらも不十分です。
- Xを確認する前にチェックポイントシーケンス値X + 1を確認した場合は、X + 1を終了し、後で再試行してください。ただし、
Identity
もちろんギャップが発生する可能性があるため(たとえば、トランザクションがロールバックされるとき)、Xが来ることはありません。 - したがって、同じアプローチですが、nミリ秒後にギャップを受け入れます。ただし、nのどの値を想定する必要がありますか?
より良いアイデアはありますか?