web-dev-qa-db-ja.com

SQLクエリは古いデータを使用しますか?どうすれば防ぐことができますか?

2つのテーブル(SJobSJobDependent)があり、それらをストアドプロシージャのロジックに結合する必要があります。どちらも1対多の関係でそれらを接続する列(job)を持っています-1つのSJob 0個以上のSJobDependentレコードのレコード。

これが私のSQLクエリです:

-- Return any records that are active and have no unsatisfied dependencies.
SELECT * FROM SJob
LEFT JOIN SJobDependent
    ON SJob.job = SJobDependent.job
    AND SJobDependent.satisfied = 0
WHERE SJobDependent.jobDependentID IS NULL
AND SJob.state = 'active'

これは、SQL Server Studioの実際の実行計画です。

SQL Server Studio Actual Execution Plan

コードの記述方法が原因で:

// Pseudo-code:

// SJob record is added with SJob.state = 'ready'.

// Related SJobDependent record(s) are added.

// SJob record is updated to SJob.state = 'active'.

これは、SQLクエリの実行時に発生するのではないかと心配しています。

  1. SJobDependentをスキャンします。
  2. SJobDependentレコードが挿入されました。
  3. SJobのスキャンを開始します。 SJob.stateは「準備完了」です。
  4. SJobを更新しました。これはSJobの読み取りをブロックしますか?
  5. SJobのスキャンを終了します。 SJob.stateは「アクティブ」です。

私が恐れている問題は、私のSQLクエリが「アクティブ」な状態で見つかったSJobレコード(SJob.state = 'active')、ただし関連するSJobDependentレコードが表示されません。

この問題は発生する可能性がありますか、それともSQLクエリを過度に分析していますか?

これが心配すべき正当な問題である場合、それを解決するにはどうすればよいですか?私は解決策を受け入れます。

私が持っている1つのアイデアは、SJobDependentのスキャンの後にSJobのスキャンを強制的に実行することです。これは可能ですか?これを行うことの影響/結果は何ですか?

実際の実行プランに表示されるスキャンは特定の順序で行われますか、それとも呼び出しごとに常にランダムですか?

注:AMtwoの回答に記載されているように、反復可能読み取り分離レベルはおそらく問題を解決しませんこれは、読み取りが開始されたときにのみ有効になる

6
Max Jacob

SQL Server(コミットされた読み取り)で既定の分離レベルを使用している場合、一貫性のない読み取りに関するあらゆる種類の問題が発生する可能性があります。ポールホワイトは、問題を説明します ここ

読み取りクエリで、特定の時点でのデータの見え方と完全に一致するデータを読み取る場合は、 Read Committed Snapshot Isolation (RCSI)を検討することをお勧めします。 RCSIを使用すると、クエリは単一の時点(クエリの開始)と整合性のあるデータを返します。ユーザーBが同時に更新を実行しているときにユーザーAがSELECTクエリを開始すると、ユーザーAはデータのsnapshotを読み取るため、「古い」値を読み取ります、これはクエリの開始と一致しています。

RCSIの欠点は、データベースレベルの設定であることです。コミットされていない読み取りとは異なり、セッションスコープの設定として設定することはできません。変更を行う前に、この変更をよりグローバルに検討する必要があります。ただし、一般的に言えば、このクエリに一貫した読み取りが必要な場合は、おそらくアプリケーション全体の一貫した読み取りが必要です。

Repeatable Read 分離レベルは問題を解決するために魅力的に見えるかもしれませんが、リンクされた投稿からこの詳細に注意してください:

反復可能な読み取り分離レベルは、データ変更されないトランザクションの存続期間中読み取られた後が初めてであることを保証します。

つまり、データはアクセスされる前に変更できますが、クエリの実行中は変更できます。また、Read Committed分離レベルと同じ一貫性のない読み取り(特にファントム)が発生することもあります。

8
AMtwo

ワークロードが全体としてどのように実行されているか、ステートメントのセットが他の人によって行われた作業をどのように見ているか、そしてそれらのステートメントをどのように見ているかを考える必要があります。また、同時に実行されている2つの作業ストリームからのアクションの可能なタイミングも考慮する必要があります。

あなたの質問からは明確ではありませんが、クエリ( "Here is my SQL query")が1つのセッション(S1)で実行され、疑似コードが別のセッション(S2)で実行されると仮定します。疑似コードを通過するたびに1つのジョブしか挿入されないと思います。問題は、S1がS2のストリームの任意の時点で完全にまたは部分的に実行できるため、S1がS2のどの作業を確認できるかということです。

「なし」と言います。これは、クエリが特にSJob.state = 'active'を検索するためです。 S2は、値が「準備完了」のSJobに挿入します。したがって、S1はこの行を読み取りません。はい、行はS1によって検査される場合があります(S1の分離レベルによって異なります)が、ステータス値のために拒否され、S1の結果セットの一部になることはありません。したがって、SJobDependentがハッシュ結合のビルド側である場合でも、取得された行はすべてFilter演算子で拒否され、S1の出力の一部になることはありません。

S2がステータスをS1が読み取れる値に設定するのは、両方のテーブルに行が存在するようになるまでです。その更新はアトミックであることが保証されています(完全に機能するか、完全にロールバックされます)。 1つの行の1つの値のみが更新されるため、それは分離されます-S1は、「準備完了」と「アクティブ」の間の値を決して見ることができません。


ステータスに関する述語がなければ、問題があった可能性があります。ただし、S1がREAD COMMITTED以上で実行されている限り、問題は発生しません。 S2は、ブロック全体に明示的なトランザクションを配置できます。次に、その排他ロックは最後まで保持されます(両方のテーブルに一貫した行のセットが存在するまで)。 S1は、これらのロックが設定される前に完了して一貫性のあるデータのみを確認するか、これらのロックが再び解放されて一貫性のあるデータを確認するまで待機します。 S1がREAD UNCOMMITTED(またはNOLOCK、同じこと)で実行されている場合、S2の作業が途中で行われ、エラーが発生します。

1
Michael Green