SQL Serverインデックス更新のデッドロック


13

同時に実行するとデッドロックが発生する2つのクエリがあります。

クエリ1-インデックス(index1)に含まれる列を更新します。

update table1 set column1 = value1 where id = @Id

table1でXロックを取得してから、index1でXロックを試行します。

クエリ2:

select columnx, columny, etc from table1 where {some condition}

index1でS-Lockを取得してから、table1でS-Lockを試行します。

同じクエリを維持しながらデッドロックを防ぐ方法はありますか?たとえば、更新前に更新トランザクションのインデックスでX-Lockを何らかの方法で取得して、テーブルとインデックスのアクセスが同じ順序であることを確認できますか?デッドロックを防ぐことができますか?

分離レベルは読み取りコミットです。インデックスの行ロックとページロックが有効になっています。同じレコードが両方のクエリに参加している可能性があります。パラメータが表示されないため、デッドロックグラフからはわかりません。

デッドロックグラフ

回答:


11

同じクエリを維持しながらデッドロックを防ぐ方法はありますか?

デッドロックグラフは、この特定のデッドロックがブックマークルックアップ(この場合はRIDルックアップ)に関連付けられた変換デッドロックであることを示しています。

デッドロックグラフ

質問が指摘しているように、クエリが異なるリソース順序で同じリソースに対して互換性のないロックを取得する可能性があるため、一般的なデッドロックのリスクが生じます。SELECT原因RIDのルックアップテーブルへのアクセスの前にクエリのニーズ指数、一方UPDATEクエリは、最初に、インデックスをテーブルを変更します。

デッドロックを解消するには、デッドロック成分の1つを除去する必要があります。主なオプションは次のとおりです。

  1. 非クラスター化インデックスをカバーすることにより、RIDルックアップを回避します。SELECTクエリは26列を返すため、これはおそらく実用的ではありません。
  2. クラスタ化インデックスを作成して、RIDルックアップを回避します。これには、列にクラスター化インデックスを作成する必要がありますProposal。これは検討する価値がありますが、この列はタイプuniqueidentifierであるように見えますが、より広範な問題に応じて、クラスター化インデックスに適した選択肢である場合とそうでない場合があります。
  3. READ_COMMITTED_SNAPSHOTまたはSNAPSHOTデータベースオプションを有効にして、読み取り時に共有ロックを取得しないでください。これには、特に設計されたブロッキング動作に関して、慎重なテストが必要になります。トリガーコードでは、ロジックが正しく実行されることを確認するためのテストも必要です。
  4. クエリのREAD UNCOMMITTED分離レベルを使用して、読み取り時に共有ロックを取得しないでくださいSELECT。通常の警告がすべて適用されます。
  5. 排他的なアプリケーションロックを使用して、問題の2つのクエリの同時実行を回避します(sp_getapplockを参照)。
  6. 同時実行性を回避するには、テーブルロックヒントを使用します。これは、質問5で特定された2つだけではなく、他のクエリに影響を与える可能性があるため、オプション5よりも大きなハンマーです。

更新前に更新トランザクションのインデックスで何らかの方法でXロックを取得して、テーブルとインデックスのアクセスが同じ順序になるようにすることはできますか

あなたは明示的なトランザクションで更新をラップして実行することにより、これを試すことができSELECTXLOCK更新前の非クラスタ化インデックス値のヒント。これは、非クラスター化インデックスの現在の値が何であるかを確実に把握し、実行計画を正しく行い、この余分なロックを取得することのすべての副作用を正しく予測することに依存します。また、冗長であると判断された場合にロックを取得するのを回避するのに十分なほどスマートではないロックエンジンに依存します

要するに、これは原則としては実現可能ですが、お勧めしません。何かを見逃したり、独創的な方法で自分を裏切るのは簡単すぎます。デッドロックを検出して再試行するのではなく、本当にこれらのデッドロックを回避する必要がある場合は、上記のより一般的なソリューションを検討することをお勧めします。


問題をさらに調査することから、変更せずに残すのがおそらく最善だと思います。私が最初に実現したより一般的な問題。
デールK

1

私は時折発生する同様の問題を抱えており、ここに私が取ったアプローチがあります。

  1. set deadlock priority low;選択に追加します。これにより、デッドロックが発生したときに、このクエリがデッドロックの犠牲になります。
  2. ブロッキングクエリが完了するまでしばらく待機/スリープした後、デッドロック(またはタイムアウト)が原因で失敗した場合、選択を自動的に再試行するようにアプリケーション内で再試行ロジックを設定します。

注:select明示的な複数ステートメントトランザクションの一部である場合は、失敗したステートメントだけでなく、トランザクション全体を再試行する必要があります。そうしないと、予期しない結果が発生する可能性があります。これが単一のselect場合は問題ありませんが、トランザクション内のステートメントxである場合は、再試行中にnすべてのnステートメントを再試行するようにしてください。


ありがとう-クエリはデフォルトで自動的にデッドロックの犠牲になります。はい、すでに堅牢な再試行メカニズムが用意されています。
デールK
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.