sql >> データベース >  >> RDS >> Database

拡張イベントを使用して実行プランの警告をキャプチャする

    今週はダブリンでIEPTO2を教えています(アイルランドがこの生涯で見る場所のリストにない場合は、追加する必要があります…ここで素晴らしいです)。今日、クエリプラン分析モジュールを終了しました。私がカバーすることの1つは、クエリプランで見つけることができる興味深いものです。例:

    • NoJoinPredicate(2005以降)
    • ColumnsWithNoStatistics(2005以降)
    • UnmatchedIndexes(2008以降)
    • PlanAffectingConvert(2012以降)

    これらの属性は、調整中に単一のプランまたは一連のプランを表示するときに探すのに適しています。ただし、もう少し積極的になりたい場合は、プランキャッシュのマイニングを開始して、そこでそれらを探すことができます。もちろん、プランはXMLであるため、これを行うにはXQueryを作成する必要があります(showplanスキーマの詳細については、http://schemas.microsoft.com/sqlserver/2004/07/showplan/を確認してください)。 XMLは好きではありませんが、試行錯誤が足りないわけではありません。参加者の1人が、拡張イベントを通じてNoJoinPredicate属性を持つクエリをキャプチャできるかどうか尋ねられたとき、次のように思いました。 。」

    案の定、そのためのイベントがあります。上記の4つすべてのイベントがあります:

    • missing_join_predicate
    • missing_column_statistics
    • unmatched_filtered_indexes
    • plan_affecting_convert

    良い。拡張イベントセッションでこれらを設定するのは非常に簡単です。この場合、event_fileターゲットを使用することをお勧めします。これは、おそらくイベントセッションを開始し、少しの間実行してから、戻って出力を確認するためです。これらのイベントが発生しないことを期待して、いくつかのアクションを含めました 多くの場合、ここではあまりオーバーヘッドを追加していません。 sql_textは、本当に信頼できるアクションではありませんが、含めました。 Jonathanはこれについて以前に説明しましたが、sql_textは単にinputbufferを提供しているだけなので、クエリの完全なストーリーを取得できない可能性があります。そのため、plan_handleも含めました。プランを探す時期によっては、プランのキャッシュに含まれなくなる可能性があることに注意してください。

    -- Remove event session if it exists
    IF EXISTS (SELECT 1 FROM [sys].[server_event_sessions]
    WHERE [name] = 'InterestingPlanEvents')
    BEGIN
      DROP EVENT SESSION [InterestingPlanEvents] ON SERVER
    END
    GO
     
    -- Define event session
    CREATE EVENT SESSION [InterestingPlanEvents]
    ON SERVER
    ADD EVENT sqlserver.missing_column_statistics
    (
      ACTION(sqlserver.database_id,sqlserver.plan_handle,sqlserver.sql_text)
      WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0))
        AND [sqlserver].[database_id]>(4))
    ),
    ADD EVENT sqlserver.missing_join_predicate
    (
      ACTION(sqlserver.database_id,sqlserver.plan_handle,sqlserver.sql_text)
      WHERE ([sqlserver].[is_system]=(0) AND [sqlserver].[database_id]>(4))
    ),
    ADD EVENT sqlserver.plan_affecting_convert
    (
      ACTION(sqlserver.database_id,sqlserver.plan_handle,sqlserver.sql_text)
      WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0))
        AND [sqlserver].[database_id]>(4))
    ),
    ADD EVENT sqlserver.unmatched_filtered_indexes
    (
      ACTION(sqlserver.plan_handle,sqlserver.sql_text)
      WHERE ([package0].[equal_boolean]([sqlserver].[is_system],(0))
        AND [sqlserver].[database_id]>(4))
    )
    ADD TARGET package0.event_file
    (
      SET filename=N'C:\temp\InterestingPlanEvents' /* change location if appropriate */
    )
    WITH (MAX_MEMORY=4096 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,
    MAX_DISPATCH_LATENCY=5 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=NONE,
    TRACK_CAUSALITY=ON,STARTUP_STATE=OFF)
    GO
     
    -- Start the event session
    ALTER EVENT SESSION [InterestingPlanEvents] ON SERVER STATE=START;
    GO

    イベントセッションを起動して実行すると、以下のサンプルコードを使用してこれらのイベントを生成できます。このコードは、AdventureWorks2014の新規インストールを前提としていることに注意してください。持っていない場合、[HumanResources]。[Employee]の[HireDate]列にクエリを実行すると、missing_column_statisticsイベントが発生しない可能性があります。

    -- These queries assume a FRESH restore of AdventureWorks2014
    ALTER DATABASE [AdventureWorks2014] SET AUTO_CREATE_STATISTICS OFF;
    GO
     
    USE [AdventureWorks2014];
    GO
     
    CREATE INDEX [NCI_SalesOrderHeader] ON [Sales].[SalesOrderHeader] (
    [PurchaseOrderNumber], [CustomerID], [TotalDue], [DueDate]
    )
    WHERE [SubTotal] > 10000.00;
    GO
     
    /*
    No join predicate
    NOTE: We clear procedure here because the event ONLY fires for the *initial* compilation
    */
    DBCC FREEPROCCACHE; /* Not for production use */
     
    SELECT [h].[SalesOrderID], [d].[SalesOrderDetailID], [h].[CustomerID]
    FROM [Sales].[SalesOrderDetail] [d],
    [Sales].[SalesOrderHeader] [h]
    WHERE [d].[ProductID] = 897;
    GO
     
    -- Columns with no statistics
    SELECT [BusinessEntityID], [NationalIDNumber], [JobTitle], [HireDate], [ModifiedDate]
    FROM [HumanResources].[Employee]
    WHERE [HireDate] >= '2013-01-01';
    GO
     
    -- Unmatched Index
    DECLARE @Total MONEY = 10000.00;
     
    SELECT [PurchaseOrderNumber], [CustomerID], [TotalDue], [DueDate]
    FROM [Sales].[SalesOrderHeader]
    WHERE [SubTotal] > @Total;
    GO
     
    -- Plan Affecting Convert
    SELECT [BusinessEntityID], [NationalIDNumber], [JobTitle], [HireDate], [ModifiedDate]
    FROM [HumanResources].[Employee]
    WHERE [NationalIDNumber] = 345106466;
    GO
     
    ALTER EVENT SESSION [InterestingPlanEvents]
    ON SERVER
    STATE=STOP;
    GO
     
    DROP EVENT SESSION [InterestingPlanEvents]
    ON SERVER;
    GO

    注:キャッシュからプランをプルし終えたら、ALTERステートメントを実行して統計の自動作成オプションを有効にすることができます。この時点でこれを行うと、プランキャッシュがクリアされ、テストを最初からやり直す必要があります。 (また、インデックスの削除が完了するまで待ちます。)

    ALTER DATABASE [AdventureWorks2014] SET AUTO_CREATE_STATISTICS ON;
    GO
     
    DROP INDEX [NCI_SalesOrderHeader] ON [Sales].[SalesOrderHeader];
    GO

    イベントセッションを停止したので、SSMSで出力ファイルを開いて、キャプチャしたものを確認します。

    拡張イベントからの出力

    結合述語が欠落している最初のクエリでは、1つのイベントが表示され、sql_textフィールドにクエリのテキストが表示されます。ただし、私が本当に望んでいるのはプランも確認することです。そのため、plan_handleを取得して、sys.dm_exec_query_planを確認できます。

    SELECT query_plan FROM sys.dm_exec_query_plan
    (0x06000700E2200333405DD12C0000000001000000000000000000000000000000000000000000000000000000);

    そして、SQL Sentry Plan Explorerでそれを開きます:

    結合述語がありません

    プランには、ネストされたループ(赤いX)に結合述語がないことを視覚的に示します。プランにカーソルを合わせると、警告が表示されます(プランのXMLに表示されます)。すばらしい…これで、このクエリの書き直しについて開発者と話をすることができます。

    次のイベントは、欠落している列の統計です。 AdventureWorks2014データベースのAUTO_CREATE_STATISTICSをオフにすることで、この状況を完全に強制しました。私はこれをいかなる方法、形、または形式でもお勧めしません。このオプションはデフォルトで有効になっているため、常に有効のままにしておくことをお勧めします。ただし、このイベントを生成するには、オフにするのが最も簡単な方法です。 sql_textフィールドにクエリがありますが、plan_handleを再度使用してプランをプルします:

    SELECT query_plan FROM sys.dm_exec_query_plan
    (0x060007004448323810921C360000000001000000000000000000000000000000000000000000000000000000);

    統計がありません

    また、計画に問題があることを示す視覚的な合図(感嘆符の付いた黄色の三角形)があり、これもXMLに含まれています。ここから、最初にAUTO_CREATE_STATISTICSが無効になっているかどうかを確認し、無効になっていない場合は、Management Studioでクエリの実行を開始して、警告を再作成できるかどうかを確認します(統計を強制的に作成します)。

    さて、残りのイベントはもう少し面白いです。

    3つのunmatched_filtered_indexesイベントがあることに気付くでしょう。理由はまだわかりませんが、現在取り組んでおり、並べ替えられたらコメントに投稿します。今のところ、イベントがあれば十分です。イベント内でオブジェクト情報も表示できるので、問題のインデックスがわかります。

    NCI_SalesOrderHeaderインデックスが欠落しているインデックスイベントによって参照されています

    そして、もう一度plan_handleを使用して、クエリプランを見つけることができます:

    一致しないインデックス

    今回はSELECT演算子に警告が表示されるので、さらに調査する必要があることがわかります。この場合、パラメータを使用しているときにオプティマイザにフィルタリングされたインデックスを使用させるオプションがあります。フィルタリングされたインデックスの使用の詳細については、Aaronの投稿を参照することをお勧めします。

    最後に、plan_affecting_convertの9つのイベントがあります。一体何?私はまだこれを理解していますが、すべてのイベントが同じタスクの一部であることを確認するために、イベントセッション(テスト時)に[因果関係の追跡]オプションを使用しました(それらは同じです)。出力の式要素を見ると、(compile_timeと同様に)わずかに変化していることがわかります。これは、SQL Sentryのプランエクスプローラーで警告の詳細を見ると明らかになります(下の2番目のスクリーンショットを参照)。イベント出力内で、式要素はします 関係する列を教えてください。これは開始点ですが、十分な情報がほとんどないため、もう一度計画を立てる必要があります。

    SELECT query_plan FROM sys.dm_exec_query_plan
    (0x0600070023747010E09E1C360000000001000000000000000000000000000000000000000000000000000000);

    変換に影響を与える計画

    プランからの変換の詳細

    SELECT演算子に友人の黄色い三角形が表示され、XML内にPlanAffectingConvert属性があります。この属性はSQLServer2012のshowplanスキーマで追加されたため、以前のバージョンを実行している場合、これはプランに表示されません。この警告を解決するには、もう少し作業が必要になる場合があります。データ型の不一致が発生している場所とその理由を理解してから、コードまたはスキーマの変更を開始する必要があります。どちらも抵抗に直面する可能性があります。 Jonathanには、暗黙的な変換について詳しく説明した投稿があります。これは、以前に変換の問題に取り組んだことがない場合に開始するのに適した場所です。

    概要

    イベントの拡張イベントライブラリは増え続けています。SQLServerでトラブルシューティングを行う際に考慮すべきことの1つは、探している情報を別の方法で取得できるかどうかです。おそらく、それがより簡単であるため(XMLよりもXEの方が好きです!)、またはより効率的であるか、より詳細な情報が得られるためです。環境内のクエリの問題を積極的に探している場合でも、誰かが報告した問題に対応しているのに問題を見つけられない場合でも、特にSQL Serverに新しい機能が追加されているため、拡張イベントを検討することをお勧めします。


    1. MariaDBの時間値から秒の部分を返す4つの関数

    2. PostgreSQLテーブルの行のカウントを高速化するにはどうすればよいですか?

    3. Oracle SQLで指定された列を選択するのはなぜですか?

    4. SQLiteでチェック制約を有効/無効にする方法