web-dev-qa-db-ja.com

スナップショット分離は良いですか?

私の状況:

テーブル:_User { Id, Name, Stone, Gold, Wood }_

私は "write"スレッドを持っています:

  • miningThread(毎分

_UPDATE User SET Stone = @calculatedValue WHERE Id=@id UPDATE User SET Wood = @calculatedValue WHERE Id=@id_

  • tradingThread(毎分

_UPDATE User SET gold = @calculatedValue WHERE Id=@id_

  • constructionThread(毎分

_UPDATE User SET Wood = @calculatedValue WHERE Id=@id UPDATE User SET Stone= @calculatedValue WHERE Id=@id_

そして、ユーザーからの "write"リクエストがあります:

  • SellResource

UPDATE User SET Stone(Wood,Gold) = @calculatedValue WHERE Id=@id

(calculatedValueはC#ビジネスロジックコードによって計算されます)

この場合、read_commited_snapshot分離レベルを設定すると、「更新が失われる」という問題がたくさん発生します。しかし、serializableまたはsnapshotレベルを設定すると、すべて正常に動作します

質問

「分離レベルの比較」の表を見ていたところ、シリアライズ可能でスナップショットの分離により、並行トランザクションに関するすべての問題が解決されることがわかりました。ただし、シリアライズ可能は非常に低速です。

  1. すべての書き込みトランザクションにスナップショット分離を使用できますか?テーブルにカオスを入れたくありません。私のビジネスロジックは難しく、常に変化しています。
  2. スナップショット分離には欠陥がありますか?
  3. 読み取り専用トランザクションにはどの分離レベルが適していますか?
7
GLeBaTi

概要:

すべての書き込みトランザクションにスナップショット分離を使用できますか?

はい。ただし、使用状況によっては、コミットされた読み取りスナップショットの方が適している場合があります。

スナップショット分離には欠陥がありますか?

はい。ディスク/メモリを必要とするすべてのアクティブなトランザクションの行バージョンを保存する必要があります。

読み取り専用トランザクションにはどの分離レベルが適していますか?

snapshot isolation、またはread committed snapshotいくつかのステートメントが相互に依存しているかどうか、およびトランザクションのサイズによって異なります。 read committed tempdbに多くの更新があり、ディスク領域が問題である場合。

より深い情報

使用する分離レベルは、ユースケースとトランザクション内でのデータの使用方法によって異なります。それでは、レベルのより深い比較から始めましょう。

SQL Serverは、ロックと行のバージョン管理という2つの異なる手法を使用して一貫性を維持します。

分離レベルのロック

ロックはSQL Serverによって機能し、読み取ったテーブル/行に共有ロックを発行して、他のトランザクションによるデータの更新をブロックします。同じことが更新にも当てはまり、排他ロックが発行され、他のトランザクションによるデータの読み取りがブロックされます。ロックは、データベースのさまざまな部分で発生します。たとえば、テーブル、行、インデックスなどです。

READ COMMITTEDは、ロックを使用して、コミットされたデータのみを読み取るようにします。そして、他のトランザクションがデータの読み取り中にデータを更新していないこと。これは、別のトランザクションが現在選択している行の更新ステートメントがブロックされていることを意味します。また、別のトランザクションが更新している行の選択ステートメントは、そのデータがコミットされるまでブロックされます。

READ UNCOMMITTEDはすべてを無視し、ロックをまったく取得しません。これは、別のトランザクションがブロックされずに現在トランザクションが読み取っている行に対して更新を実行できることを意味しますが、他のトランザクションがまだコミットしていない可能性のあるデータをトランザクションが受信するという影響もあります。

[〜#〜] serializable [〜#〜]反対を行い、すべてをロックします。 READ COMMITTEDが行を読み取ったとき、またはロックに応じてステートメントが完了したときにロックを解放しますが、SERIALIZABLEはトランザクションがコミットされるとロックを解放します。つまり、トランザクションが少なくとも1回読み取ったデータを更新する必要がある別のトランザクション、またはトランザクションが更新したデータを読み取る必要がある別のトランザクションは、トランザクションがコミットされるまでブロックされます。

行バージョン対応の分離レベル

READ COMMITTED SNAPSHOTおよびSNAPSHOT ISOLATIONは、代わりに行のバージョン管理を使用します。行のバージョン管理とは、行が変更されるたびに、SQL Serverが行のバージョンを保存し、別のトランザクションによって読み取られたときに同じままになることを意味します。

SNAPSHOT ISOLATIONは、テーブルで読み取りが行われたときに、トランザクションの開始時にコミットされた行の最新バージョンを取得する方法で機能します。これにより、トランザクション内のデータの一貫したスナップショットが提供されます。トランザクションの開始後に変更されたデータは表示されませんが、同時にトランザクションはブロックされません。失われた更新から保護するために、トランザクションは、トランザクションの開始後に別のトランザクションによって変更された一部の行を更新する場合、競合するデータのためにトランザクションを終了します。

READ COMMITTED SNAPSHOTは、スナップショット分離と同じように機能しますが、スナップショットをトランザクション全体で保持する代わりに、ステートメントの実行中のみ保持します。つまり、1つのトランザクション内の2つの読み取りステートメントが異なる結果を受け取る可能性があります。ただし、トランザクションが更新を行っているときは、以前の行バージョンの代わりに実際の行が使用されており、行が変更されたかどうかは追跡されません。

SQL Serverは、アクティブなトランザクションが使用できるように変更された各行を保持する必要があるため、それらをtempdbに格納します。このため、tempdbはすべての変更を組み込むのに十分な大きさである必要があります。まだ必要な行をチェックして残りを削除するバックグラウンドスレッドがありますが、長時間実行されているトランザクションがある場合、それらの行は削除されません。 tempdbの領域が不足すると、新しい行バージョンは作成されず、それらの(存在しない)行にアクセスしようとするトランザクションは終了します。

コメント

READ COMMITTED SNAPSHOTは、READ UNCOMMITTEDとは別に、並行性に関して最も寛容です。他のDMLステートメントをブロックせず、各ステートメント内のデータの一貫したビューを維持します。

  • SNAPSHOT ISOLATIONは許容範囲であり、READ COMMITTED SNAPSHOTよりも優れた一貫性を維持しますが、更新の際に競合解決が原因で失敗する可能性があるという欠点があります。同じトランザクション内の複数のステートメントは、互いに一貫していることが保証されています。

  • READ COMMITTEDは、同時読み取りに関しては許容範囲であり、更新についてはそうではありません。ロックのため、行のバージョン管理レベルよりも低速です。

  • ダーティデータを読み取るため、READ UNCOMMITTEDの使用はお勧めしません。これが問題にならない場合は、ロックがなく、古い行バージョンを保持する必要がないため、これが最適です。

  • SERIALIZABLEは、すべてのロックを保持しているため低速であり、同時実行性が低いため、お勧めできません。

12
Jimmy Stenke