免責事項:私は開発者です。よろしくお願いします。私は以下のことを担当する開発者ではありません。私は良い人の一人になりたいと思っています。
クライアントのSQL Server 2008R2インストールでブロックを引き起こす特定のプロセスを含むサポートチケットを継承しました。クライアントのサーバーでいつでもブロックをトリガーできますが、それを他の場所で再現することはできません。クライアントのサーバーとまったく同じハードウェア統計を備えた仮想サーバーを作成し、まったく同じデータベースをまったく同じSQL Serverセットアップに復元しましたが、サイコロはなく、複製することはできませんでした。プロセス自体は醜いです:ストアドプロシージャが呼び出され、次に名前付きのトランザクションを使用していくつかの他のストアドプロシージャを実行し、一部はカーソルにネストされます。プロセスは次の疑似コードに従います。
sp_Outermost (named transaction Trans_Outermost)
sp_Nested1 (Trans_Nested1)
sp_Nested2 (Trans_Nested2)
sp_Nested3 (Trans_Nested3)
sp_Nested3_1 (Trans_Nested3_1)
sp_Nested3_1_1 (Trans_Nested3_1_1)
sp_Nested3_1_1_1 (Trans_Nested3_1_1_1)
sp_Nested3_1_1_1_1 (Trans_Nested3_1_1_1_1)
申し訳ありません...それを説明する他の方法がわかりません。
ストアドプロシージャにはTRY-CATCH
ロジックはありませんが、GOTOs
を使用して「エラー番号」を設定する「カスタム」エラー処理があります(詳細は後ほど)。
アクティビティモニターでプロセスを見ると、タスクの状態はRUNNING
、コマンドはSELECT
、待機の種類はASYNC_NETWORK_IO
です。
DBCC OPENTRAN
を実行するか、sys.dm_tran_session_transactions
およびsys.dm_tran_active_transactions
を確認すると、最も外側のトランザクション(Trans_Outermost)が開いているトランザクションとしてリストされます。ただし、sys.dm_exec_connections
およびsys.dm_exec_sessions
に対してクエリを実行すると、実行されているクエリが実際にはsp_Nested3_1_1_1
であることが通知されます。これはalwaysの場合です。さらに、 this answer からガンクされたクエリを実行すると、待機中のステートメントがalwaysthisであることがわかります。
SET @ErrorNum = 85656
この@ErrorNum
変数は、これらのストアドプロシージャのすべてで宣言および使用されます。単純なSET
ステートメントが多くの問題を引き起こす可能性があるというのは、非常に奇妙に思えますが、偶然ではありません。
@ErrorNum
でのsp_Nested3_1_1_1
のすべての使用法をコメント化して、違いがあったかどうかを確認しました。他のすべてのストアドプロシージャから呼び出される監査ログテーブルに書き込むストアドプロシージャがあります。現在、エラーステートメントはthatプロシージャからのものですが、@ErrorNum
が含まれています。
SET @ErrorNum = 85026
だから、私の質問は、このブロッキングの根本的な原因が何であるかをどうやって理解できるのでしょうか?同じ名前のローカル変数が宣言され、ハードウェアが不十分なサーバーの入れ子の場所で使用されている場合、問題が発生する可能性がありますか?他にどこを見ることができますか?
これらの手順では85000を超えるカスタムメッセージIDのセットでRAISERROR WITH SETERROR
を使用していることを理解しました。これが重要かどうかはわかりませんが、ここで私が調べています。
Sp_Nested3_1_1_1とsp_Nested3_1_1_1_1の一部のコードをコメントアウトし、特にこの@ErrorNum
ビジネスを囲みました。これで、sp_Nested3_1_1の完全に正当なコード行のように見えるものが問題であることがわかりました。
SELECT CASE WHEN @Attached_ID = -1 THEN
SCOPE_IDENTITY()
ELSE
@Attached_ID
END AS Attached_ID
これは私には完全に恣意的であり、貧弱なハードウェアに関係があるのか、私たちに加えて他の2つのエンタープライズDBを実行しているのではないかと思います。
次のクエリとアクティビティモニターを使用して、ロックが発生するタイミングと場所を特定しています。
SELECT t.text,
QUOTENAME(OBJECT_SCHEMA_NAME(t.objectid, t.dbid)) + '.'
+ QUOTENAME(OBJECT_NAME(t.objectid, t.dbid)) proc_name,
c.connect_time,
s.last_request_start_time,
s.last_request_end_time,
s.status
FROM sys.dm_exec_connections c
JOIN sys.dm_exec_sessions s
ON c.session_id = s.session_id
CROSS APPLY sys.dm_exec_sql_text(c.most_recent_sql_handle) t
WHERE c.session_id = 61;--your blocking spid
この答え からの最初のクエリと同様です。
Q。だから、私の質問は、このブロッキングの根本的な原因を理解するにはどうすればよいですか?
あなたはそれが古い標準の1つであることに気付くかもしれません:
ただし、これをアクティビティモニターだけで追跡することは不可能ではないにしても困難になります。クライアントサーバーでいくつかの基本的な変更を行うことに耐えられる場合は、そこで何がブロックされているかを把握することに焦点を当てるべきだと思います。
ブロッキングチェーンとロックを識別し、テーブルに出力できます。ただし、最初にテーブル定義を作成してテーブルを作成する必要があります(この場合はマスターにも配置しますが、特別なツールデータベースを使用することをお勧めします)。ケンドラリトルからの抜粋です。 DECLARE @destination_table VARCHAR(4000) ; SET @destination_table = 'BLOCKED_PROCESS_REPORT' ;
DECLARE @schema VARCHAR(4000) ; EXEC sp_WhoIsActive @get_transaction_info = 1, @get_plans = 1, @find_block_leaders = 1, @RETURN_SCHEMA = 1, @SCHEMA = @schema OUTPUT ; SET @schema = REPLACE(@schema, '<table_name>', @destination_table) ; PRINT @schema EXEC(@schema) ;
Sp_configure 'blocked process threshold(s)'を実行します。 0に設定されている場合は、15(秒)などに設定して、再構成を実行します。これは通常は安全ですが、標準の購入者は警告が適用されることに注意してください。これよりも低く設定しないでください。30秒に設定すると、30秒がADO.NETのデフォルトのタイムアウトになるため、見落とす可能性があります。
Sp_WhoIsActiveを実行するエージェントジョブを作成します@get_transaction_info = 1、@ get_plans = 1、@ find_block_leaders = 1、@ destination_table = 'BLOCKED_PROCESS_REPORT'
SELECT * FROM BLOCKED_PROCESS_REPORTのクエリと上記のジョブを実行する応答を使用して、エージェントWMIアラートを作成し、WMIイベントアラートを入力します。
2つのセッションでテストします(BEGIN TRAN、INSERT into table、次にDELETE from table、他のセッションでDELETEを実行し、BLOCKED_PROCESS_REPORTテーブルに約30秒後にデータが入力され始めるのを待ちます)。
今、あなたは座って待っています。問題が再び発生すると、BLOCKED_PROCESS_REPORTに、何が何をブロックしているか、どのような順序で取得されているか、およびそこから移動できるロックについての詳細情報がたくさんあります。
完了したら、これらをクリーンアップすることを忘れないでください。
この記事 http://www.sqlshack.com/reducing-sql-server-async_network_io-wait-type/ で説明されているように、過剰なASYNC_NETWORK_IO待機タイプの値を調査している間、以下を確認する必要があります。
アプリケーションがSQL Serverインスタンスから大きなデータセットを要求しているかどうかを確認してから、クライアント側でそれらのデータをフィルター処理するかどうかを確認します。たとえば、Microsoft AccessやORMソフトウェア(別名オブジェクトリレーショナルマッピング)などのサードパーティアプリケーションがクライアント側でフィルタリングしている大きなデータセットを要求している可能性があることに注意してください。すぐに読み取りを行い、後でプログラミングする方法を使用すると、多くの場合、過度のASYNC_NETWORK_IO待機タイプの値からユーザーを節約できます。
クライアントアプリケーションに適切なビューが作成されていることを確認してください。これにより、SQL Serverインスタンスによってデータフィルタリングが確実に行われ、クライアントアプリケーションに送信されるデータ量が大幅に減少します。
アプリケーションが開いているトランザクションをコミットしていること、およびアプリケーションがタイムリーにトランザクションをコミットしていることを確認してください
SQL Serverで直接データフィルタリングを実行する方法で、要求されたデータセットを削減する方法があるかどうかを確認します
個別またはアドホッククエリの場合は、可能な限りWHERE句を追加し、要求されたデータセットを必要なデータのみに制限するようにクエリを適切に最適化してください。
クエリで「TOP n」を使用して、クエリによって返される行番号を減らすことができるかどうかを確認します
スカラー値のユーザー定義関数(UDF)は、RBARが原因でASYNC_NETWORK_IO待機タイプが高くなる原因であることが多いため、パフォーマンスに影響している可能性のあるこれらのオブジェクトのインスタンスを探します
大規模なデータベースでユーザー定義関数(UDF)で定義された計算列を使用することは、RBARが原因でASYNC_NETWORK_IO待機タイプが高くなるもう1つのよくある理由です
SQL Server 2016の場合、ネイティブにコンパイルされたUDFを使用して、ほとんどの場合RBARを大幅に下げることができ、実行速度を最大100%向上させることができます。これは、UDFをテーブル値関数にリファクタリングすることがオプションではない状況で特に役立ちます。
この情報がお役に立てば幸いです