4

これら 2 つのステートメントがリレーショナル データベースでデッドロックする可能性はありますか? 私は質問と例を単純化しようとしています-通常は共有可能な読み取りロックのみを必要とするこれらの選択には、排他的な読み取りロックが必要であると想定してください:

Concurrent Connection 1:
SELECT {...}
FROM A 
JOIN B ON {...}

Concurrent Connection 2:
SELECT {...}
FROM B 
JOIN A ON {...}

つまり、結合の順序は重要ですか? SQLアトミックの単一ステートメントはありますか? 最初のステートメントで A が最初にロックされ、次に B がロックされ、2 番目のステートメントで B が最初にロックされ、次に A がロックされますか?

私はそうは思いません - 私の直感によると、このような 2 つの単一のステートメントは、どんなに複雑であってもデッドロックすることはありません。ステートメントは全体として分析され、ロックを必要とするリソースは決定論的なグローバル順序 (つまりアルファベット順) を使用してロックされると私は信じています。しかし、これについては直感以上のものが必要です。それを証明する方法が思いつかず、ドキュメントも見つかりません。

私は MS SQL 2005 に興味がありますが、質問は実装固有のものではないと思います。

二次的に: MS SQL に関連するので、共通テーブル式にもこの保証があることを知りたいと思います.CTE は主に構文上の利点 (+ 再帰) であり、エンジンによって従来の単一のステートメントに統合されます。

4

3 に答える 3

9

SELECT は共有ロックのみを取得するため、他の SELECT とデッドロックすることはありません。あなたは、これらの SELECT が「排他的読み取りロックを必要とする」ことを考慮すべきだとおっしゃいましたが、1) のようなものは存在exlusive read lockせず、2) 読み取りは排他的ロックを取得しないため、これを考慮することはできません。

しかし、単純なステートメントがデッドロックになる可能性があるかどうかという、より一般的な質問を提起します。答えは明確で、はっきりとYESです。ロックは実行時に取得されます。前もって分析され、ソートされてから、ある順序で取得されるわけではありません。ロックはディスク上の実際のデータに依存するため、エンジンが必要なロックを前もって知ることは不可能であり、エンジンがデータをロックするために必要なデータを読み取ることは不可能です。

単純なステートメント (SELECT と UPDATE または SELECT と DELETE) の間のデッドロックは、インデックス アクセス順序の違いによるもので、非常に一般的であり、調査、診断、および修正が非常に簡単です。ただし、読み取りは互いにブロックできないため、常に書き込み操作が含まれることに注意してください。この説明では、UPDLOCK または XLOCK ヒントを SELECT に追加することは、書き込みと見なす必要があります。JOIN も必要ありません。セカンダリ インデックスは、デッドロックにつながるアクセス順序の問題を引き起こす可能性があります。 Read/Write Deadlock を参照してください。

そして最後に、書くことSELECT FROM A JOIN Bも書くことSELECT FROM B JOIN Aもまったく関係ありません。クエリ オプティマイザは、適切と思われるアクセス順序を自由に再配置できます。クエリの実際のテキストによって実行順序が強制されることはありません。

更新しました

では、デッドロックしない READ COMMITTED の「複数エンティティ」データベースに向けた一般的な戦略をどのように構築できるでしょうか?

残念ながら、クッキーカッターのレシピはありません。解決策はケースバイケースです。最終的に、データベース アプリケーションでは、デッドロックは日常茶飯事です。「月に着陸したが、正しいデータベース アプリケーションを作成できない」のように、これがばかげているように聞こえるかもしれませんが、アプリケーションが最終的にデッドロックに遭遇することをほぼ保証する強力な要因が作用しています。幸運なデッドロックはエラーに対処するのが最も簡単で、単純に状態を再度読み取り、ロジックを適用し、新しい状態を書き直します。そうは言っても、デッドロックの頻度を劇的に減らすことができるいくつかの良い方法があります。

  • Writesの一貫したアクセス パターンを持つようにしてください。Customers「トランザクションは常に次の順序でテーブルを作成する必要があります: -> OrderHeaders-> 」などのルールを明確に定義しますOrderLinestransaction内では順序に従わなければならないことに注意してください。基本的に、スキーマ内のすべてのテーブルをランク付けし、すべての更新がランキング順に発生する必要があることを指定します。これは最終的に、コードを書いている個々の貢献者のコード規律に帰着します。トランザクション内で適切な順序で書き込みが更新されることを保証する必要があるからです。
  • 書き込み時間を短縮します。通常の知恵は次のとおりです。トランザクションの開始時にすべての読み取りを行い (既存の状態を読み取ります)、ロジックを処理して新しい値を計算し、トランザクションの最後にすべての更新を書き込みます。'read->write->logic->read->write' のようなパターンは避け、代わりに 'read->read->logic->write->write' を実行します。もちろん、真の職人技は、実際の、実際の、個々のケースに対処する方法にあります。トランザクション中に書き込みを行う必要があります。ここで、特定のタイプのトランザクションについて特に注意する必要があります。キューによって駆動されるトランザクションは、定義上、キューからデキュー (= 書き込み) することによってアクティビティを開始します。これらのアプリケーションは、常に作成が難しく、エラー (特にデッドロック) が発生しやすいことで知られていましたが、幸いなことにそれを行う方法があります。 Using tables as Queues を参照してください。
  • 読み取り量を減らします。テーブル スキャンはデッドロックの最も一般的な原因です。適切なインデックス作成により、デッドロックが解消されるだけでなく、プロセスのパフォーマンスも向上する可能性があります。
  • スナップショット分離。これは、デッドロックを回避するという点で、無料のランチに最も近いものです。他の問題 (不適切なインデックス作成など) を修正する代わりに隠す可能性があるため、意図的に最後に配置しました。

LockCustomerByXXX私が恐れているアプローチでこの問題を解決しようとしてもうまくいきません。悲観的ロックはスケーリングしません。楽観的な同時実行更新は何らかの適切なパフォーマンスが必要な場合に最適な方法です。

于 2010-07-15T18:54:50.573 に答える
0

私の知る限り、あなたの言うとおりです。SQL エンジンは、何をする必要があるかを判断し (おそらくクエリを解析するときに)、必要なすべてのリソースをロックし、クエリを実行してからロックを解除します。

于 2010-07-15T17:07:20.393 に答える
0

読み取りは互いにデッドロックしません。いくつかの書き込みも行っている必要があります。

デッドロックの数を減らすためにできることがあります。たとえば、行のロックをサポートし、レコードの更新を回避するプラットフォームでは、クラスター化インデックスの最後にのみ挿入します。ああ、Facebook の UI がより理にかなったものになりました。

デッドロックを回避するよりも、デッドロックを処理する方が簡単な場合があります。サーバーは失敗し、レポートが返されます。再試行できます。

于 2010-07-15T21:42:11.347 に答える