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

SQLServer2012の拡張イベントに対する重要な変更

    他の場所で最も確実に聞いたことがあるように、SQL Server 2012は、パフォーマンスとイベントパリティの両方の点で、SQLTraceの実行可能な代替バージョンである拡張イベントのバージョンを最終的に提供します。 Management Studioで使用可能なUIなど、他にも拡張機能があります。以前は、これに対する唯一の希望はJonathanKehayiasのExtendedEventsManagerでした。アクセス許可に関連する大きな変更もあります。SQLServer2012では、ALTER ANY EVENT SESSIONのみが必要です。 拡張イベントセッションを作成および管理するため(以前はCONTROL SERVERが必要でした 。

    最近、より微妙な動作の変化に遭遇し、イベントセッションがイベントをドロップしているように見えました。変更自体は秘密ではありません。実際、この変更について何度も読んだり聞いたりした後でも(ジョナサンは、この変更についても教えてくれたことを思い出しました)、当時の最初のトラブルシューティングでは、変更を見逃していました。私が影響を与えると思った変化ではありませんでした。見よ…

    TL;DRバージョン

    SQL Server 2012では、ring_bufferを使用する場合、イベントセッションはデフォルトで1,000イベントのみをキャプチャします。 ターゲット(およびpair_matchingの場合は10,000 )。これは、メモリによってのみ制限されていた2008/2008R2からの変更です。 (この変更については、2011年7月の脚注にほぼ記載されています。)デフォルトを上書きするには、MAX_EVENTS_LIMITを使用できます。 設定–ただし、この設定はSQL Server 2008/2008 R2で認識されないため、複数のバージョンに対して機能する必要があるコードがある場合は、条件付きを使用する必要があります。

    詳細

    私が取り組んでいたシナリオはこれよりも複雑でしたが、この問題を示すために、拡張イベントの非常に単純なユースケース、つまりオブジェクトを変更しているユーザーの追跡を想定しましょう。これには便利な機能があります:object_altered 。このイベントの説明は、次のクエリから確認できます。

    SELECT description FROM sys.dm_xe_objects WHERE name = 'object_altered';
    オブジェクトがALTERステートメントによって変更されたときに発生します。このイベントは、ALTER操作ごとに2回発生します。このイベントは、操作が開始されたとき、および操作がロールバックまたはコミットされたときに発生します。 nt_usernameまたはserver_principal_nameアクションをこのイベントに追加して、誰がオブジェクトを変更したかを判別します。

    したがって、オブジェクトがたとえば20回変更された場合、40のイベントをプルすることを期待します。そして、これはまさにSQL Server 2008、2008 R2、および2012で発生することです。500を超える変更が発生した場合(1,000を超えるイベントが発生した場合)に課題が発生します。 SQL Server2008および2008R2でも、すべてのイベントをキャプチャします。ただし、SQL Server 2012は、ring_bufferの変更により、一部が削除されます。 目標。実例を示すために、イベントの損失を防ぐためにパフォーマンスを交換する簡単なサンプルイベントセッションを作成しましょう(これは、本番システムに規定するオプションのセットではないことに注意してください):

    USE master;
    GO
    CREATE EVENT SESSION [XE_Alter] ON SERVER
    ADD EVENT  sqlserver.object_altered
    (
        ACTION (sqlserver.server_principal_name)
        WHERE  (sqlserver.session_id = 78) -- change 78 to your current spid
    )
    ADD TARGET package0.ring_buffer (SET MAX_MEMORY = 4096)
    WITH (EVENT_RETENTION_MODE = NO_EVENT_LOSS, MAX_DISPATCH_LATENCY = 5 SECONDS);
     
    ALTER EVENT SESSION [XE_Alter] ON SERVER STATE = START;
    GO

    セッションが開始されたら、同じウィンドウで次のスクリプトを実行します。このスクリプトは2つのプロシージャを作成し、それらをループで変更します。

    CREATE PROCEDURE dbo.foo_x AS SELECT 1;
    GO
     
    CREATE PROCEDURE dbo.foo_y AS SELECT 1;
    GO
     
    ALTER PROCEDURE dbo.foo_x AS SELECT 2;
    GO 275
     
    ALTER PROCEDURE dbo.foo_y AS SELECT 2;
    GO 275
     
    DROP PROCEDURE dbo.foo_x, dbo.foo_y;
    GO

    次に、オブジェクト名と、各オブジェクトがターゲットから変更された回数を取得し、イベントセッションをドロップします(しばらくお待ちください。私のシステムでは、これには常に約40秒かかります):

    ;WITH raw_data(t) AS
    (
      SELECT CONVERT(XML, target_data)
      FROM sys.dm_xe_sessions AS s
      INNER JOIN sys.dm_xe_session_targets AS st
      ON s.[address] = st.event_session_address
      WHERE s.name = 'XE_Alter'
      AND st.target_name = 'ring_buffer'
    ),
    xml_data (ed) AS
    (
      SELECT e.query('.') 
      FROM raw_data 
      CROSS APPLY t.nodes('RingBufferTarget/event') AS x(e)
    )
    SELECT [object_name] = obj, event_count = COUNT(*)
    FROM
    (
      SELECT
        --[login] = ed.value('(event/action[@name="server_principal_name"]/value)[1]', 'nvarchar(128)'),
        obj   = ed.value('(event/data[@name="object_name"]/value)[1]', 'nvarchar(128)'),
        phase = ed.value('(event/data[@name="ddl_phase"]/text)[1]',    'nvarchar(128)')
      FROM xml_data
    ) AS x
    WHERE phase = 'Commit'
    GROUP BY obj;
    GO
     
    DROP EVENT SESSION [XE_Alter] ON SERVER;
    GO

    結果(Commitに焦点を当て、キャプチャされた1,000のイベントのちょうど半分を無視します イベントのみ):

    object_name event_count
    ======================
    foo_x 225
    foo_y 275

    これは、foo_xに対して50のコミットイベント(合計100のイベント)がドロップされたことを示しています。 、および正確に合計1,000のイベントが収集されました((225 + 275)* 2))。 SQL Serverは、ドロップするイベントを任意に決定しているようです。理論的には、1,000個のイベントを収集して停止した場合、foo_xに対して275個のイベントが必要になります。 、およびfoo_yの場合は225 、foo_xを変更したので 最初に、そのループが完了するまで、私はキャップを打つべきではありませんでした。しかし、明らかに、XEventsがどのイベントを保持し、どのイベントを破棄するかを決定する方法には、他にもいくつかの仕組みがあります。

    いずれの場合も、MAX_EVENTS_LIMITに別の値を指定することで、これを回避できます。 ADD TARGETで コードの一部:

    -- ...
    ADD TARGET package0.ring_buffer (SET MAX_MEMORY = 4096, MAX_EVENTS_LIMIT = 0)
    ------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^
    -- ...

    0 =無制限ですが、任意の整数値を指定できることに注意してください。上記のテストを新しい設定で実行すると、イベントがドロップされなかったため、より正確な結果が得られます。

    object_name event_count
    ======================
    foo_x 275
    foo_y 275

    上記のように、SQL Server 2008/2008 R2に対してイベントセッションを作成するときにこのプロパティを使用しようとすると、次のエラーが発生します。

    メッセージ25629、レベル16、状態1、1行目
    ターゲット「package0.ring_buffer」の場合、カスタマイズ可能な属性「MAX_EVENTS_LIMIT」は存在しません。

    したがって、何らかのコード生成を行っていて、バージョン間で一貫した動作が必要な場合は、最初にバージョンを確認し、2012以降の属性のみを含める必要があります。

    結論

    SQL Server 2008/2008 R2から2012にアップグレードする場合、または複数のバージョンを対象とする拡張イベントコードを記述した場合は、この動作の変更に注意し、それに応じてコードを記述してください。そうしないと、ドロップされたイベントが不可能であると想定し、以前の動作が示唆する状況であっても、イベントをドロップするリスクがあります。これは、UpgradeAdvisorやBestPracticesAnalyzerのようなツールが指摘するものではありません。

    この問題を取り巻く根本的なメカニズムについては、このバグレポートとこのブログ投稿で詳しく説明されています。


    1. MySQL IF()関数の説明

    2. 0〜60:間接チェックポイントへの切り替え

    3. SQL Server(T-SQL)のデータベースメールプロファイルのリストを返す

    4. オペレーター