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ロックを取得し、次にtable1でSロックを試行します。
同じクエリを維持しながらデッドロックを防ぐ方法はありますか?たとえば、テーブルとインデックスのアクセスが同じ順序であることを確認するために、更新前に更新トランザクションのインデックスでXロックをどうにかして行うことができますか?これにより、デッドロックを防ぐことができますか?
分離レベルはRead Committedです。行とページのロックがインデックスに対して有効になっています。同じレコードが両方のクエリに参加している可能性があります。パラメーターが表示されないため、デッドロックグラフからはわかりません。
同じクエリを維持しながらデッドロックを防ぐ方法はありますか?
デッドロックグラフは、この特定のデッドロックが、ブックマークルックアップ(この場合はRIDルックアップ)に関連付けられた変換デッドロックであることを示しています。
質問が指摘しているように、クエリが同じリソースに対して異なる順序で互換性のないロックを取得する可能性があるため、一般的なデッドロックリスクが発生します。 RIDルックアップのため、SELECT
クエリはテーブルの前にインデックスにアクセスする必要がありますが、UPDATE
クエリは最初にテーブルを変更し、次にインデックスを変更します。
デッドロックを解消するには、デッドロックの成分の1つを取り除く必要があります。主なオプションは次のとおりです。
SELECT
クエリは26列を返すため、これはおそらく実用的ではありません。Proposal
にクラスター化インデックスを作成する必要があります。これは検討に値しますが、この列はuniqueidentifier
タイプであると思われます。これは、より広範な問題に応じて、クラスター化インデックスに適している場合とそうでない場合があります。READ_COMMITTED_SNAPSHOT
またはSNAPSHOT
データベースオプションを有効にして、読み取り時に共有ロックを取得しないようにします。これには、特に設計されたブロッキング動作に関して、慎重なテストが必要になります。トリガーコードには、ロジックが正しく実行されることを確認するためのテストも必要です。SELECT
クエリにREAD UNCOMMITTED
分離レベルを使用して、読み取り時に共有ロックを取得しないでください。通常の警告がすべて適用されます。テーブルとインデックスのアクセスが同じ順序であることを確認するために、更新前に更新トランザクションのインデックスで何らかの方法でXロックを取得できますか
これを試すには、明示的なトランザクションで更新をラップし、更新前に非クラスター化インデックス値に対してSELECT
ヒントを使用してXLOCK
を実行します。これは、非クラスター化インデックスの現在の値が何であるかを確実に把握し、実行計画を正しく立て、この追加のロックを取得することによるすべての副作用を正しく予測することに依存しています。 冗長であると判断された場合 の場合、ロックを回避するのに十分なほど賢くないロックエンジンにも依存します。
つまり、これは基本的には実現可能ですが、お勧めしません。何かを見逃したり、独創的な方法で自分を出し抜くのは簡単です。 (単に検出して再試行するのではなく)これらのデッドロックを本当に回避する必要がある場合は、代わりに上記のより一般的な解決策を検討することをお勧めします。
私は時々発生する同様の問題があり、ここに私が取るアプローチがあります。
set deadlock priority low;
を選択します。これにより、デッドロックが発生したときに、このクエリがデッドロックの犠牲になります。注:select
が明示的なマルチステートメントトランザクションの一部である場合、失敗したステートメントだけでなく、トランザクション全体を再試行する必要があります。そうしないと、予期しない結果が発生する可能性があります。これが単一のselect
の場合は問題ありませんが、トランザクション内のx
のステートメントn
の場合は、再試行中にすべてのn
ステートメントを必ず再試行してください。