8

TableBtoの結合を実行するストアド プロシージャがありますTableA

 SELECT <--- Nested <--- TableA
             Loop   <--
                      |
                      ---TableB

同時に、トランザクションでは、行が に挿入されTableA、次に に挿入されTableBます。

この状況は、ストアド プロシージャ select が TableB から行を取得し、insert がTableAに行を追加し、それぞれが他のテーブルを手放すことを望んでいるため、デッドロックを引き起こすことがあります。

INSERT     SELECT
=========  ========
Lock A     Lock B
Insert A   Select B
Want B     Want A
....deadlock...

ロジックでは、INSERT最初に行をAに追加してからBに追加する必要がありますが、個人的には、SQL Server が結合を実行する順序は気にしません。

デッドロックを修正するための一般的な推奨事項は、全員が同じ順序でリソースにアクセスできるようにすることです。しかし、この場合、SQL Server のオプティマイザは、逆の順序が「より良い」と言っています。別の結合順序を強制して、クエリのパフォーマンスを低下させることができます。

しかし、私はすべきですか?

使用したい結合順序で、オプティマイザーを今も永遠にオーバーライドする必要がありますか?

または、エラーネイティブ エラー 1205をトラップして、選択ステートメントを再送信する必要がありますか?

問題は、オプティマイザーをオーバーライドして、最適でないことを行うと、クエリのパフォーマンスがどれほど低下するかということではありません。問題は、より悪いクエリを実行するよりも、自動的に再試行する方がよいかどうかです。

4

3 に答える 3

9

デッドロックを自動的に再試行する方が良いですか。その理由は、このデッドロックを修正しても、後で別のデッドロックに遭遇する可能性があるためです。テーブルのサイズが変更された場合、サーバーのハードウェア仕様が変更された場合、サーバーの負荷が変更された場合でも、SQL のリリース間で動作が変わる可能性があります。デッドロックが頻繁に発生する場合は、それを解消するための積極的な措置を講じる必要があります (通常はインデックスが解決策です) が、まれなデッドロック (たとえば 10 分ごとなど) の場合は、アプリケーションでの再試行によってデッドロックが隠される可能性があります。もちろん、書き込みは適切な開始トランザクション/コミットトランザクションに囲まれているため、すべての書き込み操作をアトミックに保ち、問題なく再試行できるため、読み取りまたは書き込みを再試行できます。

考慮すべきもう 1 つの方法は、read commit スナップショットを有効にすることです。これを有効にすると、SELECT は単純にロックを取得しませんが、一貫した読み取りが得られます。

于 2010-03-04T22:20:10.963 に答える
5

デッドロックを回避するための最も一般的な推奨事項の 1 つは、「同じ順序でロックを取得する」または「同じ順序でオブジェクトにアクセスする」ことです。明らかにこれは完全に理にかなっていますが、常に実行可能ですか? いつでも可能ですか?このアドバイスに従えないケースに遭遇し続けています。

オブジェクトを 1 つの親テーブルと 1 つ以上の子テーブルに格納すると、このアドバイスにはまったく従えません。挿入するときは、最初に親行を挿入する必要があります。削除するときは、逆の順序で行う必要があります。

複数のテーブルまたは 1 つのテーブル内の複数の行にアクセスするコマンドを使用する場合、通常、ロックを取得する順序を制御できません (ヒントを使用していないと仮定します)。

そのため、多くの場合、同じ順序でロックを取得しようとしても、すべてのデッドロックを防ぐことはできません。したがって、いずれにせよ何らかのデッドロック処理が必要です。デッドロックをすべて排除できるとは想定できません。もちろん、Service Broker または sp_getapplock を使用してすべてのアクセスをシリアル化しない限り。

デッドロック後に再試行すると、他のプロセスの変更を上書きする可能性が非常に高くなります。私たちが変更しようとしていたデータを他の誰かが変更した可能性が非常に高いことに注意する必要があります。特に、すべてのリーダーがスナップショット分離で実行されている場合、リーダーがデッドロックに関与することはありません。つまり、デッドロックに関与するすべての関係者はライターであり、同じデータを変更または変更しようとしています。例外をキャッチして自動的に再試行すると、他の人の変更を上書きできます。

これは失われた更新と呼ばれ、通常は間違っています。通常、デッドロック後に行うべき正しいことは、はるかに高いレベルで再試行することです。データを再選択し、最初の保存の決定と同じ方法で保存するかどうかを決定します。

たとえば、ユーザーが [保存] ボタンを押して、保存中のトランザクションがデッドロックの対象として選択された場合、デッドロック後のデータを画面に再表示することをお勧めします。

于 2010-03-05T01:32:42.467 に答える
2

トラップと再実行は機能しますが、SELECT が常にデッドロックの犠牲になると確信していますか? 挿入がデッドロックの犠牲者である場合は、再試行についてさらに注意する必要があります。

この場合の最も簡単な解決策は、NOLOCK または READUNCOMMITTED (同じこと) を選択することだと思います。人々はダーティ リードについて当然の懸念を抱いていますが、私たちは何年にもわたって並行性を高めるためにあらゆる場所で NOLOCK を実行してきましたが、問題が発生したことは一度もありません。

また、ロックのセマンティクスについてもう少し調査したいと思います。たとえば、トランザクション分離レベルをスナップショット (2005 以降が必要) に設定すると、問題が解決すると思います。

于 2010-03-04T21:57:14.603 に答える