0

私の開発環境では、MSSQL 2005 で直面している運用上の問題を再現しようとしています。この問題には 2 つの部分があります。

問題

1) デッドロックが発生し、MSSQL は 1 つの接続 (「接続 X」) を「被害者」として選択します。2) 「接続 X」を使用する後続の試みはすべて失敗します (接続プーリングを使用します)。MSSQL は「サーバーはトランザクションを再開できませんでした」と言う

2 つのうち、より深刻な場合は #2: 「接続 X」は「接続 X」を再利用しようとするすべての「ラウンド ロビン」試行が失敗するため、ミステリアスな「ランダム」エラーがユーザーに表示されます。サーバーを再起動する必要があります。

私が書く理由

ただし、この時点で、問題 1 を再現したいと思います。簡単にデッドロックを作成できます。

しかし、ここに私の問題があります: 本番環境では、MSSQL は 1 つの接続 (SPID) を「デッドロックの犠牲者」として選択しますが、私のテスト環境では、デッドロックがハングするだけです...そしてハングしてハングします。永遠に?よくわかりませんが、一晩ぶら下げたままにしておいて、朝になってもまだぶら下がっていました。

では、ここで質問です。デッドロックが発生したときに、SQL サーバーに「デッドロックの犠牲者を選択」させるにはどうすればよいでしょうか。

これまでの試み

jdbc url (「lockTimeout=5000」) を介して「lock_timeout」パラメーターを設定しようとしましたが、本番環境とは異なるメッセージが表示されました (テストでは、「ロック要求のタイムアウト期間を超えました。」本番環境では「トランザクション (プロセス) ID 59) は、別のプロセスとのロック リソースでデッドロックされ、デッドロックの犠牲者として選択されました。")

問題#2の詳細

この「トランザクションを再開できません」という問題を調査したところ、いくつかのことがわかりました。

  • 不適切な例外処理がこの問題を引き起こす可能性があります。例: Java コードは Statement/PreparedStatement を閉じず、ドライバーの「接続」の実装は、不良/古い/古い「トランザクション ID」でスタックしています。
  • jdbc ドライバーをアップグレードすると、問題が解決する場合があります。

ただし、今のところ、デッドロックを再作成し、SQL サーバーに「デッドロックの犠牲者を選択」させたいだけです。

前もって感謝します!

付録 A. 技術環境

発達:

  • SQL サーバー 2005 SP3 (9.00.4035.00)
  • ドライバー: sqljdbc.jar バージョン 1.0
  • JBoss 3.2.6
  • jdbc URL: jdbc:sqlserver://<>;

製造:

  • SQL サーバー 2005 SP2 (9.00.3042.00)
  • ドライバー: sqljdbc.jar バージョン 1.0
  • JBoss 3.2.6
  • jdbc URL: jdbc:sqlserver://<>;

付録 B. デッドロックを強制する手順

  • 接続 A を取得する
  • 接続 B を取得する
  • 接続Aでsql1を実行します
  • 接続 B で sql2 を実行します
  • 接続 B で sql1 を実行します
  • 接続Aでsql2を実行します

where sql1: update member set name = name + 'x' WHERE member_id = 71

sql2: update member set name = name + 'x' WHERE member_id = 72

4

4 に答える 4

0

を使用して、セッションのデッドロック優先度を指定できます。

SET DEADLOCK_PRIORITY LOW | MEDIUM | HIGH

詳細については、このMSDNリンクを参照してください。

次のコマンドを使用して、開いているトランザクションを表示することもできます

DBCC OPENTRAN (db_name)

このコマンドは、デッドロックの原因を特定するのに役立つ場合があります。詳細については、 MSDNを参照してください。

于 2009-08-27T15:29:41.797 に答える
0

[これは回答への応答です。UI では、回答に対するより長い「コメント」は許可されていません]

実行されているクエリは何ですか? 実際にデッドロックを引き起こしているのは何ですか?

私のテスト環境では、非常に単純なクエリを実行しました。

sql1: UPDATE プリンシパル SET 名 = 名前 + '.' WHERE プリンシパル ID = 71

sql2: UPDATE プリンシパル SET 名 = 名前 + '.' WHERE プリンシパル ID = 72

次に、それらを chiastic/criss-cross の順序で、つまりコミットなしで実行しました。

接続A

 sql1

   connectionB

      sql2

      sql1

 sql2

これはデッドロックの基本的な例のように思えます。ただし、これがデッドロックではなく「単なるロック」である場合は、この概念の乱用をやめてください。

本番環境では、「問題のあるクエリ」(「prodbad」) は次のように見えました。

UPDATE ポスト SET lock_flag = ? WHERE thread_id IN (SELECT thread_id FROM POST WHERE post_id = ?)

いくつかのことに注意してください。

1) この「prod problem query」は実際に機能します。私の知る限り、今回はデッドロックが発生しました

2) 問題はページのロック、つまりトランザクションの他の場所での読み取りによる悲観的ロックにあると思われます。

3) このクエリの前に、このトランザクションが実行した SQL がわかりません。

4 ) このクエリは、「1 つの SQL ステートメントでそれを行うことができる」処理の例です。これは、プログラマーには巧妙に見えますが、最終的には 2 つのクエリを実行するよりもはるかに多くの IO が発生します。

queryM:SELECT thread_id FROM POST WHERE post_id = ?

queryN: UPDATE ポスト SET lock_flag = ? WHERE スレッド ID = <>

*>(考えてみると、開発データベースは本番 DB のコピーですか?

Dev DB が Prod DB よりも桁違いに小さい場合、クエリは同じかもしれませんが、SQL が「内部」で行うことは大きく異なります。)*

この場合、prod データベースと dev データベースは異なります。「製品サーバー」には大量のデータがありました。「Dev db」にはほとんどデータがありませんでした。クエリは非常に異なっていました。私がやりたかったのは、デッドロックを再現することだけでした。

*> サーバーはトランザクションの再開に失敗しました... なぜ?. JDBにアップグレードする必要があります

C SQL ドライバー v2.0 は何よりも優先されます。*

ありがとう。この変更を予定しています。ドライバーを切り替えると少しリスクが生じるため、テストを実行する必要があります..

要点をまとめると:

単純なデッドロックを強制し、接続が「強打/ホース/ボーク/など」であるかどうかを確認する「素晴らしいアイデア」がありました。ただし、デッドロックは本番環境とは異なる動作をしました。

于 2009-08-28T17:00:26.333 に答える
0

実行されているクエリは何ですか? 実際にデッドロックを引き起こしているのは何ですか?

A と B の 2 つの接続があるとします。A は sql1 を実行してから sql2 を実行し、B は sql2 を実行してから sql1 を実行します。では、どのような作業 (クエリ) が行われているのでしょうか? さらに重要なことは、トランザクションがどこにあるのか? どの分離レベルを使用していますか? 取引を開始/終了するものは何ですか? (はい、これはあなたのドライバーが使用する例外処理に疑問を投げかけることにつながります-もし彼らが返された「動作しませんでした」メッセージを検出して適切に処理しないなら、あなたは絶対にそれらを取り出して撃つ必要があります--弾丸またはペニシリン、あなたの電話.)

デッドロックの根底にある明示的な詳細を理解すると、デッドロックを再現できます。最初に、アプリケーションの「下」に再作成しようとします。つまり、SSMS で 2 つのウィンドウを開き、必要に応じて手動でアプリケーションのアクションを段階的に再作成します。これができるようになったら、一歩下がって、アプリケーションでそれを複製します。もちろん、すべて開発サーバー上でです!

(考えてみると、開発データベースは本番 DB のコピーですか? 開発 DB が本番 DB よりも桁違いに小さい場合、クエリは同じかもしれませんが、SQL が「内部」で行うことは大きく異なります。)

最後の考えとして、SQL はデッドロックを自動的に検出して処理します (これを無効にできるとは本当に思いません)。あなたが一晩中実行している場合、デッドロックはないと思いますが、従来のロック/ブロックの問題です。

[今これを投稿します -- 何か調べに行きます。後で確認します。]
[後で]

興味深いことに、SQL Server 2005 コンパクト エディションはデッドロックを検出せず、タイムアウトのみを検出します。あなたはDevでそれを使用していませんよね?

デッドロックのタイムアウト期間を「オフにする」または制御する方法がわかりません。私は先週、デッドロックに遭遇してめちゃくちゃになりました. その後、いくつかの任意のテストで、(開発サーバーの場合)5秒以内にデッドロックが検出され解決されることが示されました. 開発マシンにデッドロックがなく、ブロックしているようです。しかし、この問題は「安楽椅子の DBA」にとって分析が難しいことを認識してください。この問題が発生しているときに、システム内で何が起こっているかを真剣に分析する必要があります。

于 2009-08-27T15:47:01.497 に答える
0

JDBc 接続が正しくない状態になる理由の説明は、次のとおりです。 サーバーはトランザクションの再開に失敗しました... なぜですか? . 何よりもまず、 JDBC SQL ドライバー v2.0にアップグレードする必要があります。このリンクには、この状況を回避するためにアプリケーション処理を修正する方法に関するアドバイスも含まれています。最も重要なのは、JDBC トランザクション API とネイティブの Transact-SQL トランザクションが混在しないようにすることです。

デッドロックの再現については、テストでデッドロックを再現しませんでした。トランザクションがコミットされるのを待ってブロックしました。デッドロックは別のものであり、SQL Server被害者を選択します。デッドロックの優先度やロック タイムアウトなどを設定する必要はありません。デッドロックの優先度はまったく別のトピックであり、優先度の高い夜間バッチ処理と優先度の低い夜間バッチ処理など、特定のシナリオで犠牲者を選択するために使用されます。

デッドロックの調査は、デッドロックを解消したい場合は、デッドロックを理解することから始める必要があります。プロファイラーのデッドロック グラフ イベント クラスは、出発点として最適です。デッドロック グラフ情報を使用すると、デッドロックが発生しているリソースと関連するステートメントを確認できます。ほとんどの場合、解決策は、アプリケーションの更新の順序を修正する (常に同じ順序に従う) か、アクセス パスを修正する (つまり、インデックスを追加する) ことです。

アップデート

  • UPDATE .. WHERE key IN (SELECT ...)操作はアトミックではないため、通常はデッドロックが発生します。SELECT 部分は何もロックしないため、複数のスレッドが同じIN リストを返す可能性があります。これは推測にすぎません。適切に検証するには、デッドロック情報を確認する必要があります。

  • 手作りのデッドロック テストを検証するには、ブロックしている SPID がループを形成していることを検証する必要があります。を見てくださいSELECT session_id, blocking_session_id FROM sys.dm_exec_requests WHERE blocking_session_id <> 0。結果にループ (例: A が B によってブロックされ、B が A によってブロックされる) が含まれ、サーバーがデッドロックをトリガーしない場合、それはバグです。ただし、ブロックリストはループを形成せず、A が B によってブロックされ、B が C によってブロックされ、C がリストにないものになることがわかります。これは、再現テストで何か間違ったことをしたことを意味します。

于 2009-08-27T16:41:27.423 に答える