32

MS SQL Server 2005 上で実行されている .NET 2.0 webapp で、非常にまれではありますが厄介な SQL デッドロックに遭遇しています。これまで、私たちは非常に経験的な方法で SQL デッドロックに対処してきました。

しかし、このアプローチは非常に不十分であることがわかりました。時間がかかり、信頼性がありません。私は、SQL デッドロックが決して発生しないように設計されている決定論的なクエリ パターンに従うことを強く望んでいます。

たとえば、C# のマルチスレッド プログラミングでは、辞書式の順序に従ってロックを取得する必要があるなどの単純な設計ルールにより、デッドロックが発生しないことが保証れます。

デッドロック防止が保証されている SQL コーディング パターンはありますか?

4

10 に答える 10

22

デッドロック防止コードを書くのは本当に大変です。同じ順序でテーブルにアクセスしても、デッドロックが発生する場合があります [1]。デッドロックの状況を回避して解決するのに役立ついくつかのアプローチについて詳しく説明した記事をブログに書きました。

2 つのステートメント/トランザクションがデッドロックにならないようにしたい場合は、sp_lockシステム ストアド プロシージャを使用して、各ステートメントが消費するロックを観察することで実現できる場合があります。これを行うには、非常に高速であるか、ホールドロック ヒントを使用してオープン トランザクションを使用する必要があります。


ノート:

  1. 一度に複数のロックを必要とする SELECT ステートメントは、逆の順序でロックを取得するインテリジェントに設計されたトランザクションに対してデッドロックする可能性があります。
于 2008-09-21T21:41:25.333 に答える
13

ゼロ デッドロックは基本的に、実行中のすべてのトランザクション (これには SELECT を含む) に対して読み取りおよび変更する予定のすべてのテーブル/obj を知っている必要があるため、一般的なケースでは信じられないほどコストのかかる問題です。一般的な考え方は、順序付けられた厳密な 2 フェーズ ロックと呼ばれます (2 フェーズ コミットと混同しないでください) ( http://en.wikipedia.org/wiki/Two_phase_locking ; 2PL でさえデッドロックがないことを保証しません)。

厳密な 2PL を実際に実装している DBMS はほとんどありません。これは、すべてのトランザクションが単純な SELECT ステートメントの実行を待機している間、パフォーマンスに大きな影響を与えるためです (フリー ランチはありません)。

いずれにせよ、これに本当に興味がある場合はSET ISOLATION LEVEL、SQL Server を参照してください。必要に応じて微調整できます。http://en.wikipedia.org/wiki/Isolation_level

詳細については、シリアル化可能性に関するウィキペディアを参照してください: http://en.wikipedia.org/wiki/Serializability

つまり、ソース コードのリビジョンのようなものです。早い段階で頻繁にチェックインしてください。トランザクションを小さく (SQL ステートメントの数、変更された行の数)、高速に保ちます (ウォール クロック時間は、他のトランザクションとの衝突を回避するのに役立ちます)。1 回のトランザクションで多くのことを行うのは適切で整頓されているかもしれませんが、一般的にはその哲学に同意しますが、多くのデッドロックが発生している場合は、トランザクションをより小さなトランザクションに分割してから、移動しながら、アプリケーションでステータスを確認してください。TRAN 1 - OK Y/N? Y の場合、TRAN 2 を送信 - OK Y/N? などなど

余談ですが、私が DBA であり、(数千人の同時ユーザーを測定するマルチユーザー DB アプリの) 開発者でもある長年の経験の中で、デッドロックがそれほど大きな問題であることに気付いたことは一度もありませんでした。レベルは意地悪など)。

于 2008-09-22T02:34:06.127 に答える
3

実際に機能する、この問題に対する魔法の汎用ソリューションはありません。アプリケーションに並行性をプッシュすることはできますが、これは非常に複雑になる可能性があります。特に、別のメモリ空間で実行されている他のプログラムと調整する必要がある場合はそうです。

デッドロックの機会を減らすための一般的な回答:

  1. 基本的なクエリの最適化 (適切なインデックスの使用)、ホットスポットを回避する設計、可能な限り短い時間でトランザクションを保持するなど。

  2. 可能であれば、合理的なクエリ タイムアウトを設定して、デッドロックが発生した場合にタイムアウト期間が経過した後に自動的にクリアされるようにします。

  3. MSSQL のデッドロックは、多くの場合、デフォルトの読み取り同時実行モデルが原因であるため、それに依存しないことが非常に重要です。すべての設計で Oracle スタイルの MVCC を想定してください。スナップショット分離を使用するか、可能であれば READ UNCOMMITED 分離レベルを使用します。

于 2009-02-08T05:36:22.813 に答える
2

次の便利な読み取り/書き込みパターンは、いくつかの制約が与えられた場合、デッドロックの証拠になると思います。

制約:

  1. 1 つのテーブル
  2. 読み取り/書き込みにはインデックスまたは PK が使用されるため、エンジンはテーブル ロックに依存しません。
  3. 単一の SQL where 句を使用して、レコードのバッチを読み取ることができます。
  4. SQL Server 用語の使用。

書き込みサイクル:

  1. 単一の「Read Committed」トランザクション内のすべての書き込み。
  2. トランザクションの最初の更新は、各更新グループ内の特定の常に存在するレコードに対するものです。
  3. その後、複数のレコードを任意の順序で書き込むことができます。(それらは最初のレコードへの書き込みによって「保護」されます)。

読み取りサイクル:

  1. デフォルトの read commit トランザクション レベル
  2. 取引なし
  3. 単一の select ステートメントとしてレコードを読み取ります。

利点:

  1. 2 次書き込みサイクルは、最初の書き込みトランザクションが完全に完了するまで、最初のレコードの書き込みでブロックされます。
  2. 読み取りは、書き込みコミット間でアトミックにブロック/キュー/実行されます。
  3. 「Serializable」に頼らずに、トランザクション レベルの一貫性を実現します。

これも機能する必要があるので、コメント/修正してください!!

于 2011-11-14T19:57:28.740 に答える
1

アプリに対して十分な設計制御がある場合は、更新/挿入を特定のストアドプロシージャに制限し、アプリが使用するデータベースロールから更新/挿入権限を削除します(これらのストアドプロシージャを介した更新のみを明示的に許可します)。

データベース接続をアプリ内の特定のクラスに分離し(すべての接続はこのクラスからのものである必要があります)、「クエリのみ」の接続が分離レベルを「ダーティリード」に設定するように指定します...すべての結合で(nolock)に相当します。

このようにして、(特定のストアドプロシージャに対して)ロックを引き起こす可能性のあるアクティビティを分離し、「ロックループ」から「単純な読み取り」を取り除きます。

于 2008-09-22T02:49:50.973 に答える
1

誰も言及していないこと (驚くべきことに) は、SQL サーバーに関する多くのロックの問題は、DB のクエリ ワークロードをカバーするインデックスの適切なセットで排除できるということです。なんで?テーブルのクラスター化インデックス (ヒープではない場合) へのブックマーク ルックアップの数を大幅に削減できるため、競合とロックが減少します。

于 2008-11-03T01:23:25.140 に答える
1

あなたが言ったように、常に同じ順序でテーブルにアクセスすることは、デッドロックを回避するための非常に良い方法です。さらに、トランザクションを可能な限り短縮します。

もう 1 つのクールなトリックは、可能な限り 2 つの SQL ステートメントを 1 つに結合することです。単一ステートメントは常にトランザクションです。たとえば、「UPDATE ... SELECT」または「INSERT ... SELECT」を使用し、「SELECT COUNT」または「IF (EXISTS ...)」の代わりに「@@ERROR」および「@@ROWCOUNT」を使用します。

最後に、構成可能な回数だけクエリを再ポストすることで、呼び出し元のコードがデッドロックを処理できることを確認します。これは通常の動作であり、アプリケーションで対処できる必要があります。

于 2008-09-21T18:57:20.733 に答える
1

ロック取得の一貫したシーケンスに加えて、もう 1 つの方法は、ロックと分離のヒントを明示的に使用して、読み取り中に意図しない共有などのロックを意図せずに取得して浪費される時間/リソースを削減することです。

于 2008-09-22T05:05:06.703 に答える
0

簡単な答えはノーです。保証された技術はありません。

自明ではないスループットがある場合、設計原則として、アプリケーションのデッドロック防止を一般的に行う方法がわかりません。プロセスで必要になる可能性のあるすべてのリソースを、最終的には必要としない場合でも、同じ順序で先制的にロックすると、2 番目のプロセスが必要な最初のロックの取得を待機するという、よりコストのかかる問題が発生するリスクがあります。 、およびあなたの可用性が影響を受けます。また、システム内のリソースの数が増えると、些細なプロセスでも、デッドロックを防ぐためにすべてを同じ順序でロックする必要があります。

ほとんどのパフォーマンスや可用性の問題と同様に、SQL デッドロックの問題を解決する最善の方法は、プロファイラーでワークロードを調べて動作を理解することです。

于 2008-09-22T03:43:13.280 に答える
0

あなたの質問への直接的な答えではありませんが、考えてみてください:

http://en.wikipedia.org/wiki/Dining_philosophers_problem

「食事哲学者問題」は、デッドロック問題を調べるための古い思考実験です。それについて読むことは、あなたの特定の状況に対する解決策を見つけるのに役立つかもしれません.

于 2008-09-22T05:55:18.733 に答える