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

SQL Server 2016:sys.dm_exec_function_stats

    SQL Server 2016 CTP 2.1には、CTP2.0以降に登場した新しいオブジェクトsys.dm_exec_function_statsが1つあります。これは、sys.dm_exec_procedure_stats、sys.dm_exec_query_stats、およびsys.dm_exec_trigger_statsと同様の機能を提供することを目的としています。 これで、ユーザー定義関数の集計ランタイムメトリックを追跡できるようになりました。

    それとも?

    少なくともCTP2.1では、ここでは通常のスカラー関数に対して意味のあるメトリックを導出することしかできませんでした。インラインまたはマルチステートメントTVFには何も登録されていませんでした。とにかく実行前に本質的に拡張されるので、インライン関数については驚かない。しかし、マルチステートメントTVFはパフォーマンスの問題であることが多いため、それらも表示されることを期待していました。それらはまだsys.dm_exec_query_statsに表示されるため、そこからパフォーマンスメトリックを導出できますが、作業の一部を実行する複数のステートメントが実際にある場合、集計を実行するのは難しい場合があります。何もロールアップされません。

    これがどのように機能するかを簡単に見てみましょう。 100,000行の単純なテーブルがあるとしましょう:

    SELECT TOP (100000) o1.[object_id], o1.create_date
      INTO dbo.src
      FROM sys.all_objects AS o1
      CROSS JOIN sys.all_objects AS o2
      ORDER BY o1.[object_id];
    GO
    CREATE CLUSTERED INDEX x ON dbo.src([object_id]);
    GO
    -- prime the cache
    SELECT [object_id], create_date FROM dbo.src;
    をプライムします

    スカラーUDF、マルチステートメントのテーブル値関数、およびインラインテーブル値関数を調査したときに何が起こるか、およびそれぞれの場合にどのような作業が行われたかをどのように確認するかを比較したいと思いました。まず、SELECTでできる些細なことを想像してみてください ただし、日付を文字列としてフォーマットするなど、区分化する必要がある場合があります。

    CREATE PROCEDURE dbo.p_dt_Standard
      @dt_ CHAR(10) = NULL
    AS
    BEGIN
      SET NOCOUNT ON;
      SELECT @dt_ = CONVERT(CHAR(10), create_date, 120)
        FROM dbo.src
        ORDER BY [object_id];
    END
    GO

    (出力を変数に割り当てます。これにより、テーブル全体が強制的にスキャンされますが、パフォーマンスメトリックが、出力を消費およびレンダリングするSSMSの作業によって影響を受けるのを防ぎます。リマインダーのMikael Erikssonに感謝します。)

    >

    多くの場合、その変換を関数に変換する人がいます。これは、次のようにスカラーまたはTVFにすることができます。

    CREATE FUNCTION dbo.dt_Inline(@dt_ DATETIME)
    RETURNS TABLE
    AS
      RETURN (SELECT dt_ = CONVERT(CHAR(10), @dt_, 120));
    GO
     
    CREATE FUNCTION dbo.dt_Multi(@dt_ DATETIME)
    RETURNS @t TABLE(dt_ CHAR(10))
    AS
    BEGIN
      INSERT @t(dt_) SELECT CONVERT(CHAR(10), @dt_, 120);
      RETURN;
    END
    GO
     
    CREATE FUNCTION dbo.dt_Scalar(@dt_ DATETIME)
    RETURNS CHAR(10)
    AS
    BEGIN
      RETURN (SELECT CONVERT(CHAR(10), @dt_, 120));
    END
    GO

    これらの関数の周りにプロシージャラッパーを次のように作成しました:

    CREATE PROCEDURE dbo.p_dt_Inline
      @dt_ CHAR(10) = NULL
    AS
    BEGIN
      SET NOCOUNT ON;
      SELECT @dt_ = dt.dt_
        FROM dbo.src AS o
        CROSS APPLY dbo.dt_Inline(o.create_date) AS dt
        ORDER BY o.[object_id];
    END
    GO
     
    CREATE PROCEDURE dbo.p_dt_Multi
      @dt_ CHAR(10) = NULL
    AS
    BEGIN
      SET NOCOUNT ON;
      SELECT @dt_ = dt.dt_
        FROM dbo.src
        CROSS APPLY dbo.dt_Multi(create_date) AS dt
        ORDER BY [object_id];
    END
    GO
     
    CREATE PROCEDURE dbo.p_dt_Scalar
      @dt_ CHAR(10) = NULL
    AS
    BEGIN
      SET NOCOUNT ON;
      SELECT @dt_ = dt = dbo.dt_Scalar(create_date)
        FROM dbo.src
        ORDER BY [object_id];
    END
    GO

    (いいえ、dt_ あなたが見ている慣習は、私が良い考えだと思う新しいものではありません。それは、DMV内のこれらすべてのクエリを収集されている他のすべてから分離できる最も簡単な方法でした。また、ストアドプロシージャ内のクエリとアドホックバージョンを簡単に区別するために、サフィックスを簡単に追加できるようになりました。)

    次に、タイミングを格納する#tempテーブルを作成し、このプロセスを繰り返しました(ストアドプロシージャを2回実行し、プロシージャの本体を分離されたアドホッククエリとして2回実行し、それぞれのタイミングを追跡します):

    CREATE TABLE #t
    (
      ID INT IDENTITY(1,1), 
      q VARCHAR(32), 
      s DATETIME2, 
      e DATETIME2
    );
    GO
     
    INSERT #t(q,s) VALUES('p Standard',SYSDATETIME());
    GO
     
    EXEC dbo.p_dt_Standard;
    GO 2
     
    UPDATE #t SET e = SYSDATETIME() WHERE ID = 1;
    GO
     
    INSERT #t(q,s) VALUES('ad hoc Standard',SYSDATETIME());
    GO
     
    DECLARE @dt_st CHAR(10);
      SELECT @dt_st = CONVERT(CHAR(10), create_date, 120)
        FROM dbo.src
        ORDER BY [object_id];
    GO 2
     
    UPDATE #t SET e = SYSDATETIME() WHERE ID = 2;
    GO
    -- repeat for inline, multi and scalar versions

    次に、いくつかの診断クエリを実行しました。結果は次のとおりです。

    sys.dm_exec_function_stats

    SELECT name = OBJECT_NAME(object_id), 
      execution_count,
      time_milliseconds = total_elapsed_time/1000
    FROM sys.dm_exec_function_stats
    WHERE database_id = DB_ID()
    ORDER BY name;

    結果:

    name        execution_count    time_milliseconds
    ---------   ---------------    -----------------
    dt_Scalar   400000             1116

    これはタイプミスではありません。スカラーUDFのみが新しいDMVに存在することを示します。

    sys.dm_exec_procedure_stats

    SELECT name = OBJECT_NAME(object_id), 
      execution_count,
      time_milliseconds = total_elapsed_time/1000
    FROM sys.dm_exec_procedure_stats
    WHERE database_id = DB_ID()
    ORDER BY name;

    結果:

    name            execution_count    time_milliseconds
    -------------   ---------------    -----------------
    p_dt_Inline     2                  74
    p_dt_Multi      2                  269
    p_dt_Scalar     2                  1063
    p_dt_Standard   2                  75

    これは驚くべき結果ではありません。スカラー関数を使用すると、パフォーマンスが大幅に低下しますが、マルチステートメントTVFは約4倍悪化しました。複数のテストにわたって、インライン関数は、関数がまったくない場合よりも常に同じか1ミリ秒または2秒高速でした。

    sys.dm_exec_query_stats

    SELECT 
      query = SUBSTRING([text],s,e), 
      execution_count, 
      time_milliseconds
    FROM
    (
      SELECT t.[text],
        s = s.statement_start_offset/2 + 1,
        e = COALESCE(NULLIF(s.statement_end_offset,-1),8000)/2,
        s.execution_count,
        time_milliseconds = s.total_elapsed_time/1000
      FROM sys.dm_exec_query_stats AS s
      OUTER APPLY sys.dm_exec_sql_text(s.[sql_handle]) AS t
      WHERE t.[text] LIKE N'%dt[_]%' 
    ) AS x;

    切り捨てられた結果、手動で並べ替え:

    query (truncated)                                                       execution_count    time_milliseconds
    --------------------------------------------------------------------    ---------------    -----------------
    -- p Standard:
    SELECT @dt_ = CONVERT(CHAR(10), create_date, 120) ...                   2                  75
    -- ad hoc Standard:
    SELECT @dt_st = CONVERT(CHAR(10), create_date, 120) ...                 2                  72
     
    -- p Inline:
    SELECT @dt_ = dt.dt_ FROM dbo.src AS o CROSS APPLY dbo.dt_Inline...     2                  74
    -- ad hoc Inline:
    SELECT @dt_in = dt.dt_ FROM dbo.src AS o CROSS APPLY dbo.dt_Inline...   2                  72
     
    -- all Multi:
    INSERT @t(dt_) SELECT CONVERT(CHAR(10), @dt_, 120);                     184                5
    -- p Multi:
    SELECT @dt_ = dt.dt_ FROM dbo.src CROSS APPLY dbo.dt_Multi...           2                  270
    -- ad hoc Multi:
    SELECT @dt_m = dt.dt_ FROM dbo.src AS o CROSS APPLY dbo.dt_Multi...     2                  257
     
    -- all scalar:
    RETURN (SELECT CONVERT(CHAR(10), @dt_, 120));                           400000             581
    -- p Scalar:
    SELECT @dt_ = dbo.dt_Scalar(create_date)...                             2                  986
    -- ad hoc Scalar:
    SELECT @dt_sc = dbo.dt_Scalar(create_date)...                           2                  902

    ここで注意すべき重要な点は、マルチステートメントTVFのINSERTとスカラー関数のRETURNステートメントの時間(ミリ秒)も個々のSELECT内で考慮されるため、すべてを合計するだけでは意味がないということです。タイミング。

    手動タイミング

    そして最後に、#tempテーブルからのタイミング:

    SELECT query = q, 
        time_milliseconds = DATEDIFF(millisecond, s, e) 
      FROM #t 
      ORDER BY ID;

    結果:

    query             time_milliseconds
    ---------------   -----------------
    p Standard        107
    ad hoc Standard   78
    p Inline          80
    ad hoc Inline     78
    p Multi           351
    ad hoc Multi      263
    p Scalar          992
    ad hoc Scalar     907

    ここでの追加の興味深い結果:プロシージャラッパーには常にある程度のオーバーヘッドがありましたが、それがどれほど重要であるかは本当に主観的かもしれません。

    概要

    今日の私のポイントは、新しいDMVの動作を示し、期待値を正しく設定することでした。関数のパフォーマンスメトリックの中には、誤解を招くものもあれば、まったく利用できないものもあります(または、少なくとも自分でつなぎ合わせるのは非常に面倒です)。 )。

    ただし、この新しいDMVは、SQL Serverが以前に欠落していたクエリ監視の最大の部分の1つをカバーしていると思います。スカラー関数は、使用法を識別する唯一の信頼できる方法がクエリテキストを解析することであったため、パフォーマンスを低下させることがあります。絶対確実というわけではありません。それではパフォーマンスへの影響を分離できないという事実や、そもそもクエリテキストでスカラーUDFを探していることを知っている必要があるという事実を気にしないでください。

    付録

    スクリプトを添付しました:DMExecFunctionStats.zip

    また、CTP1の時点で、列のセットは次のとおりです。

    database_id object_id type type_desc
    sql_handle plan_handle cached_time last_execution_time execution_count
    total_worker_time last_worker_time min_worker_time max_worker_time
    total_physical_reads last_physical_reads min_physical_reads max_physical_reads
    total_logical_writes last_logical_writes min_logical_writes max_logical_writes
    total_logical_reads last_logical_reads min_logical_reads max_logical_reads
    total_elapsed_time last_elapsed_time min_elapsed_time max_elapsed_time

    現在sys.dm_exec_function_statsにある列


    1. SQLの「AND」または「OR」が最初に来ますか?

    2. SQL CASEステートメント:それは何であり、それを使用するための最良の方法は何ですか?

    3. SQL Serverのストアドプロシージャの列情報を返す:sp_sproc_columns

    4. SQL Server Management Studio(SSMS)でクエリ結果を.csvまたはタブ区切りファイルにエクスポートする方法-SQL Server/TSQLチュートリアルパート23