web-dev-qa-db-ja.com

ストアドプロシージャのいくつかの層を実行するプロセスのブロックを診断する

免責事項:私は開発者です。よろしくお願いします。私は以下のことを担当する開発者ではありません。私は良い人の一人になりたいと思っています。

クライアントの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

この答え からの最初のクエリと同様です。

3
AJ.

Q。だから、私の質問は、このブロッキングの根本的な原因を理解するにはどうすればよいですか?

あなたはそれが古い標準の1つであることに気付くかもしれません:

  • コードの同時実行の問題。
  • 再構築を行うメンテナンスジョブ。
  • レポート作成(またはExcel!)のためにデータベースに直接接続しているユーザーがブロックを引き起こしています。

ただし、これをアクティビティモニターだけで追跡することは不可能ではないにしても困難になります。クライアントサーバーでいくつかの基本的な変更を行うことに耐えられる場合は、そこで何がブロックされているかを把握することに焦点を当てるべきだと思います。

  1. Adam Mechanicのsp_WhoIsActiveをシステムに配置します。ツールを保持するためだけのデータベースがない場合は、どこにでも実行できるようにこれをマスターに配置することをお勧めします。
  2. ブロッキングチェーンとロックを識別し、テーブルに出力できます。ただし、最初にテーブル定義を作成してテーブルを作成する必要があります(この場合はマスターにも配置しますが、特別なツールデータベースを使用することをお勧めします)。ケンドラリトルからの抜粋です。 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) ;

  3. Sp_configure 'blocked process threshold(s)'を実行します。 0に設定されている場合は、15(秒)などに設定して、再構成を実行します。これは通常は安全ですが、標準の購入者は警告が適用されることに注意してください。これよりも低く設定しないでください。30秒に設定すると、30秒がADO.NETのデフォルトのタイムアウトになるため、見落とす可能性があります。

  4. Sp_WhoIsActiveを実行するエージェントジョブを作成します@get_transaction_info = 1、@ get_plans = 1、@ find_block_leaders = 1、@ destination_table = 'BLOCKED_PROCESS_REPORT'

  5. SELECT * FROM BLOCKED_PROCESS_REPORTのクエリと上記のジョブを実行する応答を使用して、エージェントWMIアラートを作成し、WMIイベントアラートを入力します。

  6. 2つのセッションでテストします(BEGIN TRAN、INSERT into table、次にDELETE from table、他のセッションでDELETEを実行し、BLOCKED_PROCESS_REPORTテーブルに約30秒後にデータが入力され始めるのを待ちます)。

今、あなたは座って待っています。問題が再び発生すると、BLOCKED_PROCESS_REPORTに、何が何をブロックしているか、どのような順序で取得されているか、およびそこから移動できるロックについての詳細情報がたくさんあります。

完了したら、これらをクリーンアップすることを忘れないでください。

4
Cody Konior

この記事 http://www.sqlshack.com/reducing-sql-server-async_network_io-wait-type/ で説明されているように、過剰なASYNC_NETWORK_IO待機タイプの値を調査している間、以下を確認する必要があります。

  1. アプリケーションがSQL Serverインスタンスから大きなデータセットを要求しているかどうかを確認してから、クライアント側でそれらのデータをフィルター処理するかどうかを確認します。たとえば、Microsoft AccessやORMソフトウェア(別名オブジェクトリレーショナルマッピング)などのサードパーティアプリケーションがクライアント側でフィルタリングしている大きなデータセットを要求している可能性があることに注意してください。すぐに読み取りを行い、後でプログラミングする方法を使用すると、多くの場合、過度のASYNC_NETWORK_IO待機タイプの値からユーザーを節約できます。

  2. クライアントアプリケーションに適切なビューが作成されていることを確認してください。これにより、SQL Serverインスタンスによってデータフィルタリングが確実に行われ、クライアントアプリケーションに送信されるデータ量が大幅に減少します。

  3. アプリケーションが開いているトランザクションをコミットしていること、およびアプリケーションがタイムリーにトランザクションをコミットしていることを確認してください

  4. SQL Serverで直接データフィルタリングを実行する方法で、要求されたデータセットを削減する方法があるかどうかを確認します

  5. 個別またはアドホッククエリの場合は、可能な限りWHERE句を追加し、要求されたデータセットを必要なデータのみに制限するようにクエリを適切に最適化してください。

  6. クエリで「TOP n」を使用して、クエリによって返される行番号を減らすことができるかどうかを確認します

  7. スカラー値のユーザー定義関数(UDF)は、RBARが原因でASYNC_NETWORK_IO待機タイプが高くなる原因であることが多いため、パフォーマンスに影響している可能性のあるこれらのオブジェクトのインスタンスを探します

  8. 大規模なデータベースでユーザー定義関数(UDF)で定義された計算列を使用することは、RBARが原因でASYNC_NETWORK_IO待機タイプが高くなるもう1つのよくある理由です

  9. SQL Server 2016の場合、ネイティブにコンパイルされたUDFを使用して、ほとんどの場合RBARを大幅に下げることができ、実行速度を最大100%向上させることができます。これは、UDFをテーブル値関数にリファクタリングすることがオプションではない状況で特に役立ちます。

この情報がお役に立てば幸いです

3
MarcelO