IT技術互動交流平臺

SQLServer擴展事件(ExtentedEvents)從入門到進階(4)擴展事件引擎基本概念

來源:IT165收集  發布日期:2016-12-22 20:36:26

本文屬于 SQL Server 擴展事件(Extented Events)從入門到進階 系列

 

在第一二節中,我們創建了一些簡單的、類似典型SQL Trace的擴展事件會話。在此過程中,介紹了很多擴展事件基礎組件,包括事件、謂詞、操作和目標。本節,將對擴展事件引擎、架構和基本組件做更加深入的了解。通過這些講解,可以大概了解到為什么擴展事件相對于SQL Trace來說更加低開銷。另外,還會延時如何設計事件會話從而最小化事件收集過程中的不必要開銷,即使這些事件會話會很復雜。

 

事件數據收集生命周期:

擴展事件(Extended Events,XE)中的事件,可以發生在SQL Server進程中的很多地方,每個事件在遇到特定的事件代碼時都會觸發。比如有些事件是在存儲過程開始執行或者編譯時觸發、遇到死鎖時觸發、統計信息自動更新時觸發、鎖申請或釋放時觸發等。 當正在執行的任務觸發事件時,我們希望能夠簡單地,從任務執行線程直接傳入XE引擎。它必須為每個活動事件做以下處理:收集事件的基本負載數據。評估謂詞如果謂詞為True則收集對應操作。直接分派事件數據到所有同步目標或者到中間內存緩沖區等待分派到異步目標。 一旦所有數據被收集完畢。控制流程返回執行線程。前面提到過,XE引擎是重新設計和重寫現有高開銷的Trace架構。其目標就是最小化事件采集過程中的性能壓力,比如最小化在采集過程中,任務執行線程放棄控制以便事件采集數據可以順利進行的時間。 在第一節中提到過,擴展事件主要使用下面方式來實現最小化影響:最小化在默認負載(相對于Trace這種收集事件所有列而言)中收集的事件列數。在事件數據采集之前使用謂詞預先過濾掉不必要的數據。通過先進的預聚合(pre-aggregating)可用性目標有時候可以顯著降低引擎可能需要收集的數據總量。 但是不管怎樣,在創建事件會話是還是需要考慮很多東西以免像SQL Trace那樣傷害SQL Server性能。下面將詳細介紹各個相關組件。

 

擴展事件引擎基礎架構:

XE引擎,屬于SQLOS的一部分,是一個用于創建事件對象集合、事件會話創建、管理及處理事件數據捕獲的服務。前面提到過的組件:特別是事件、操作、謂詞和目標,都不屬于核心引擎。這些對象,以及類型和映射,都存在于包(packet)里面,包駐留在SQL Server的各種模組(modules)中,如exe文件、DLLs等。這些模組(如sqlos.dll、sqldk.dll、sqlservr.exe)在實例啟動時,把擴展事件引擎一并注冊,然后引擎通過包進行交互,使得不同的事件、操作、謂詞、目標、映射和類型可用。

模組和包(Modules and Packages):

包被加載到XE引擎從而為我們在定義事件會話時使用的不同的XE對象提供服務。下面第一個語句用于查找現有的包,并列出從加載的模組列表:
SELECT  [p].[name] AS [Module] ,        [p].[description] AS [Description] ,        [m].[name] AS [ModulePath]FROM    [sys].[dm_xe_packages] [p]        JOIN [sys].[dm_os_loaded_modules] [m]        ON [p].[module_address] = [m].[base_address];GO

下面是本機的結果:
如果是在SQL 2016上運行,會有13行。而SQL 2008只有4行。本機是SQL 2014。下面的語句用于顯示每個包加載了哪些事件:
SELECT  [xo].[name] AS [EventName] ,
        [xo].[description] AS [EventDescription] ,
        [xp].[name] AS [Package]
FROM    [sys].[dm_xe_objects] [xo]
        JOIN [sys].[dm_xe_packages] [xp]
        ON [xo].[package_guid] = [xp].[guid]
WHERE   [xo].[object_type] = N'event'
ORDER BY [xo].[name];
GO

本機結果如下:


事件:

擴展事件,正如其名,其基礎組件當然就是“事件(events)”,我們可以創建沒有任何操作、謂詞甚至目標的事件會話,但是必須有最少一個事件。 SQL 2016 CU1提供了1303種不同的事件,以“通道(channel)”形式組織。這四個channel為:Admin、Operational、AnalyticDebug。沒有必要記住哪個事件屬于哪個channel,但是注意默認情況下只有前面三個channel是GUI默認顯示的。而Debug在GUI中除非手動顯示否則默認不現實的,并且沒有辦法使其默認顯示。sys.dm_xe_objects 視圖可以顯示所有channel的所有事件。 當你添加一個事件到事件會話時,每當事件會話開始運行,并且特定的事件被觸發,控制器都會把事件數據傳到擴展事件引擎中。 每當事件觸發時,第一個操作就是收集每個事件會話中定義的默認元素集數據。也就是每個事件的默認負載。默認負載是事件的最小列集,并且這個默認負載通常隨著事件的不同而不同。這種方式通過只收集必要的數據從而減少整體開銷。在Trace中,事件的所有列都會被收集然后再根據篩選條件篩選。 下面腳本用于收集某個事件的默認數據元素,比如sql_statement_completed事件:
SELECT  [object_name] AS [EventName] ,        [name] AS [ElementName] ,        [column_id] AS [ColumnID] ,        [type_name] AS [ElementType] ,        [column_type] AS [ColumnType] ,        [capabilities_desc] AS [Capability] ,        [description] AS [ElementDescription]FROM    [sys].[dm_xe_object_columns]WHERE   [object_name] = N'sql_statement_completed'        AND [column_type] <> 'readonly';GO
除了腳本形式,還可以用GUI方式查看,比如第二篇中提到的【事件】頁,默認負載是不可定制化的,除了下圖的紅框那些:



事件列(也叫數據列)包含了列類型。我們可以把它們作為默認負載的一部分來收集,當然也可以不收集。 雖然擴展事件通常來說開銷都比Trace小,但是也還是有一些事件會明顯影響性能。比如showplan就是特別需要注意的,比如下圖中紅框部分明確提醒了:


即使加上了謂詞篩選,showplan依舊具有很高開銷,因為showplan_xml字段是屬于默認負載。因此,在謂詞生效之前,必須收集執行計劃的XML數據,這個操作是一個高開銷操作。

操作:

如果我們需要收集事件數據中的其他不在默認負載中的列,或者希望事件觸發時同時觸發另外一個操作,那么就需要顯式指定一個合適的操作到事件會話中。 一旦事件觸發,XE引起會收集事件的默認負載(包括里面已經啟用的可選列)。并對事件會話進行謂詞評估。只有事件觸發并符合謂詞定義的數據才會被收集,并且只有此時被 執行的操作才會被收集,這種設計還是為了最小化負載。下面腳本用于返回可用的操作:
SELECT  [xp].[name] AS [Package] ,        [xo].[name] AS [Action] ,        [xo].[description] AS [Description]FROM    [sys].[dm_xe_packages] AS [xp]        JOIN [sys].[dm_xe_objects] AS [xo]        ON [xp].[guid] = [xo].package_guidWHERE   ( [xp].[capabilities] IS NULL          OR [xp].[capabilities] & 1 = 0        )        AND ( [xo].[capabilities] IS NULL              OR [xo].[capabilities] & 1 = 0            )        AND [xo].[object_type] = 'action';GO

在GUI中,這些操作顯示在【全局字段(操作)】當中。可以把上面語句返回的列表中的操作加到你的事件會話定義中,但有些特定的操作可能在某些事件觸發時不可用,比如“query_plan_hash”操作對“sp_statement_starting”事件不可用。【操作】用于收集對事件有用的額外信息,但是需要考慮引入的額外開銷。默認負載屬于事件的一部分,所以事件觸發時數據已經可用。對于符合事件定義的謂詞為True的操作,會“同步”執行。意味著XE引擎必須在任務執行線程中收集這些數據或者執行其他操作。根據操作的類型及數量,或多或少會影響性能。比如,添加database_id這個操作,比添加tsql_stack操作開銷就低很多。同時,根據執行的頻率,和查詢本身(比如用了用戶自定義函數),tsql_stack操作可能影響任務執行。另外,操作畢竟增加了事件所收集的數據量,并且一些操作需要更多的存儲空間(如callstack 操作就需要比cpu_id更多的存儲空間)。所以在配置事件會話時,記得指定事件的最大大小。如果這個值太小,可能收集不到事件數據。 有些操作(action)還會執行一些額外的操作(operation,比如收集事件的狀態信息、收集內存轉儲(memory dump)。這些副作用操作,比如內存轉儲或調試斷點,不應該用于常規事件會話。只有在極端情況下才應該在生產環境中使用。

謂詞:

謂詞在事件觸發時起到篩選作用,相對于SQL Trace,擴展事件的謂詞具有強大和細粒度的篩選能力。在Trace中,設置過濾條件,依舊會針對Trace中的所有事件生效。在XE中,可以對每個事件做獨立的謂詞定義。同時Trace中,無法實現謂詞的AND/OR限定,而XE的其中一個關鍵特性就是能夠盡快地執行“短路”事件,使事件盡快地返回控制任務執行的線程。更深層次而言,XE的謂詞是在事件觸發時馬上生效,僅會收集滿足謂詞邏輯的操作,這也是XE優于Trace(收集后過濾)的另外一個地方。 下面語句演示了一個非常簡單的謂詞,確保事件會話可以收集sp_statement_completed事件,但是只有邏輯讀大于10000的語句才收集。而對于小于這個值的語句,使用短路邏輯過濾掉,不執行任何操作(actions):
CREATE EVENT SESSION [MyEventSession] ON SERVERADD EVENT sqlserver.sp_statement_completed (  SET collect_object_name = ( 1 ) ,                                              collect_statement = ( 1 )    ACTION ( sqlserver.client_app_name, sqlserver.database_name )    WHERE ( [logical_reads] >= ( 10000 ) ) );GO

我們可以使用布爾表達式來創建謂詞的邏輯塊(logical blocks),這些邏輯塊非常關鍵的,因為一旦謂詞塊中的邏輯評估為false,那么評估會停止,同時事件不觸發。下面語句對上面的事件會話定義添加了一個AND邏輯,為了觸發事件,邏輯讀必須大于等于10000并且持續事件必須小于1秒(1000000 微妙)。
CREATE EVENT SESSION [MyEventSession] ON SERVERADD EVENT sqlserver.sp_statement_completed (  SET collect_object_name = ( 1 ) ,                                              collect_statement = ( 1 )    ACTION ( sqlserver.client_app_name, sqlserver.database_name )    WHERE ( [logical_reads] >= ( 10000 )            AND [duration] >= ( 1000000 )          ) );GO

持續時間可以為微妙或毫秒,可以通過下面語句來確定:
SELECT
   [object_name] AS [Event],
   [name] AS [Column],
   [description] AS [Description]
FROM  [sys].[dm_xe_object_columns]
WHERE [name] = 'duration'
ORDER BY [object_name];

本機結果如下:

如果任何謂詞檢測為False,那么事件都不會觸發,這里就要注意謂詞的順序。比如前面的例子中,logical_reads在第一個,意味著如果語句的邏輯讀小于10000,那么就會發生短路邏輯,直接跳過對持續事件的評估。如果條件反過來,那么會先檢測在一個存儲過程中任何語句是否小于1秒,如果都小于1秒,同樣會短路,不評估邏輯讀。通過這種方式可以降低收集的開銷。 前面的例子中,我們只是對事件的默認負載中的事件字段定義謂詞。但是我們同樣可以對全局字段(操作)定義謂詞。由于操作的發生在任務執行線程中是和事件觸發同步的,所以XE引擎在這個過程中也必須評估操作的謂詞。如果這些操作中的謂詞評估也會false,那么事件的數據收集就會終止。如果為True,那么所有操作都會被執行,然后把數據分派到目標或者中間內存緩沖區中。下面這個代碼就是一個例子:
CREATE EVENT SESSION [MyEventSession] ON SERVER
ADD EVENT sqlserver.sp_statement_completed (  SET collect_object_name = ( 1 ) ,
                                              collect_statement = ( 1 )
    ACTION ( sqlserver.client_app_name, sqlserver.database_name )
    WHERE ( [sqlserver].[database_id] = ( 7 )
            AND [logical_reads] >= ( 10000 )
            AND [duration] >= ( 1000 )
          ) );
GO

database_id首先觸發,XE引擎收集database_id數據列,并只對值為7的觸發。如果我們需要收集的大部分語句都不在這個數據庫中,那么這個列作為謂詞的第一列就不夠高效了。

映射(Maps):

在T-SQL中編寫謂詞條件需要對映射(Maps)有一定的了解。映射表示在引擎中常用值的名稱與另一種格式的關系。比如wait_info事件,用于獲取SQL Server中關于等待信息的內容。如果你想獲取關于“WRITELOG”等待的信息,并且持續時間大于1秒,你需要添加wait_info事件到事件會話中,然后在“WRITELOG”中wait_type配置謂詞。在UI界面中這個操作倒不是什么問題,但是如果用T-SQL來編寫,或者也擔心謂詞最大字符限制(3000個字符),那么就需要把WRITELOG映射成數值型格式。 可以使用下面語句查找相關信息:
SELECT  [xmv].[name] ,
        [xmv].[map_key] ,
        [xmv].[map_value]
FROM    sys.dm_xe_map_values [xmv]
        JOIN sys.dm_xe_packages [xp]
        ON [xmv].[object_package_guid] = [xp].[guid]
WHERE   [xmv].[name] = N'wait_types';
GO

本機結果如下: 語句返回了所有“wait_type”的“map_key”和他對應的“map_values”及對應的描述。然后我們就可以把上面的要求改寫成:
CREATE EVENT SESSION [Capture WRITELOG Waits] ON SERVER
ADD EVENT sqlos.wait_info (
    WHERE ( ( [wait_type] = ( 181) )
            AND ( [duration] >= ( 1000 ) )
          ) )
ADD TARGET package0.event_file ( SET filename = N'C:	empWaitInfo' );
GO

如果使用復雜和很長的謂詞,使用map_key會更加有效。但是由于這個映射關系可能在補丁升級時變更,所以使用時需要校驗。

目標:

在配置了合適的事件、操作和謂詞之后,我們通常需要選擇目標來存儲事件會話采集的數據。【目標】是一個事件的“消費者”,是事件數據的最終目的地。目標可以同步地消耗事件,如在相同線程中觸發事件,或者異步地消耗。在擴展事件中有6類可用目標:event_file:類似SQL Trace中的Trace文件的文件系統目標,在事件會話結束后固化到硬盤中。ring_buffer:使用FIFO(First In First Out,先進先出)算法,把數據駐留在內存存儲中。event_counter:在內存中,每個事件會話中的事件出現頻率數。histogram:在內存中的目標,產生一個關于事件發生的柱狀圖,關于所有常用事件的每個數據列或者操作,或者一個特定事件或者操作的一列的信息。pair_matching:在內存中,匹配目標的開始和結束事件,只保留沒有配對的那些事件(比如有事務開始事件,沒有事務結束事件) 除此之外還有一個“Event Tracing For Windows(ETW)”目標,用于微軟團隊做深入研究,所以在這里不打算介紹。對于event_file和ring_buffer目標,都是異步的,并且以原始數據形式采集。而event_counter目標是同步的,僅收集一個事件符合謂詞的觸發次數。 histogram和pair-matching目標也是異步的,并且不存儲原始數據格式,但是以某些方式存儲聚合數據,這個方式基于配置。

event_file:

event_file目標存在硬盤,并且事件在會話啟用時開始寫入硬盤。在事件會話停止后固化到硬盤中,可用于后續分析。這個數據以二進制格式存儲數據,并且在GUI顯示時以XML鏈接顯示。 在SQL 2008/2008R2中,event_file數據存儲在兩個文件中,一個保存數據,一個保存元數據,在SQL 2012及以上版本,只有一個.xel文件,當事件引擎創建文件目標時,會在文件名后面追加一個數值型,標識從事件啟動到1700-1-1開始的描述。確保文件名創建時是唯一的。 在創建event_file目標時,應該設置最大文件大小,也應該設置【文件滾動更新】中的【最大文件數】。

ring_buffer:

ring_buffer目標存在內存中,基于FIFO算法存儲數據,ring_buffer的最大緩沖區內存大小按MB計算,同時也可以指定要保留的事件數。需要知道,由于默認負載和某些配置操作,有些事件可能不能適應內存分配的大小。當ring_buffer滿了,舊的事件數據會被移除,新的事件數據會被載入。 因為數據以二進制存儲,所以必須使用XQuery分析ring_buffer中的數據。根據事件發生頻率和內存分配的數量,可以通過不停的查詢可以顯示不同的數據。同時,所有存在于ring_buffer的數據不能被顯示,因為sys.dm_xe_session_targets DMV中的一個問題,不能超過4MB。基于這個原因。ring_buffer數據一旦事件會話結束即被清出內存。所以ring_buffer不適合用于綜合分析事件數據。




Event_counter:


event_counter是異步的,僅收集事件觸發的次數,并且前提是滿足謂詞的事件。這個目標是內存駐留的,同樣事件停止后,數據也會清空。這個目標通常用于驗證事件會話是否使用了合適的事件(簡單來說就是用于Troubleshooting事件配置)。也可以在你對服務器的負載不確定的情況下用于協助檢測。 使用這個目標可以看出事件基于謂詞的觸發頻率。

Histogram:




這個目標在SQL 2008/2008R2中稱為“bucketizer”,因為它基于選擇的元素進行分組,比如你想知道為什么語句重編譯,可以使用下面語句捕獲:
CREATE EVENT SESSION [Capture WRITELOG Waits] ON SERVER
ADD EVENT sqlos.wait_info (
    WHERE ( ( [wait_type] = ( 181 ) )
            AND ( [duration] >= ( 1000 ) )
          ) )
ADD TARGET package0.event_file ( SET filename = N'C:	empWaitInfo' );
GO

CREATE EVENT SESSION [TrackRecompiles] ON SERVER
ADD EVENT sqlserver.sql_statement_recompile (  SET collect_object_name = ( 1 ) ,
                                               collect_statement = ( 1 )
    WHERE ( [sqlserver].[is_system] = ( 0 ) ) )
ADD TARGET package0.histogram (  SET filtering_event_name = N'sqlserver.sql_statement_recompile' ,
                                 slots = ( 11 ) ,
                                 source = N'recompile_cause' ,
                                 source_type = ( 0 ) )
WITH ( MAX_MEMORY = 4096 KB ,
        EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS ,
        MAX_DISPATCH_LATENCY = 30 SECONDS ,
        MAX_EVENT_SIZE = 0 KB ,
        MEMORY_PARTITION_MODE = NONE ,
        TRACK_CAUSALITY = OFF ,
        STARTUP_STATE = OFF );
GO

輸出到GUI的結果講列出每個非系統重編譯事件的recompile_cause(數值)和一個觸發的時間值。一旦會話停止,數據就被移除。其中一個限制就是你只能在一個元素上進行分組。在前面提到過,對那些查找哪些object被重編譯時,對object_id分組很有用。如果希望對多個元素分組,只能使用event_file或ring_buffer,然后手動計算。同時,在histogram目標中,只能有256個唯一buckets(由于中文版SSMS也沒有對這個詞進行翻譯,所以這里也保留原詞)。如上面語句中的slots=(11),顯示了11個不同的重編譯原因。

Pair_matching:

pair_matching目標需要兩個事件,開始事件和結束事件。當開始事件沒有匹配一個結束事件,就會被記錄在目標中。事件中的一個或多個元素可以被匹配,這些元素非常重要,因為可以用于確保事件是否丟失或者捕獲異常。需要注意的是,這個“開始”和“結束”事件,不總是一一對應的關系。比如lock acquired和lock released事件,在鎖升級的時候,關系就不是一一對應了。


使用pair_matching的常見用處是偵測超時和一些attention問題,可以使用sql_statement_starting和sql_statement_completed事件。注意匹配不總是在session_id上,也可以在tsql_stack上,需要兩個一起使用:
CREATE EVENT SESSION [Find_Unmatched_Statements] ON SERVER
ADD EVENT sqlserver.sql_statement_starting (
    ACTION ( sqlserver.session_id, sqlserver.tsql_stack ) ),
ADD EVENT sqlserver.sql_statement_completed (
    ACTION ( sqlserver.session_id, sqlserver.tsql_stack ) )
ADD TARGET package0.pair_matching  (  SET begin_event = N'sqlserver.sql_statement_starting' ,
        begin_matching_actions = N'sqlserver.session_id, sqlserver.tsql_stack' ,
        end_event = N'sqlserver.sql_statement_completed' ,
        end_matching_actions = N'sqlserver.session_id, sqlserver.tsql_stack' ,
        respond_to_memory_pressure = ( 0 )
 )
WITH (  MAX_MEMORY = 4096 KB ,
        EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS ,
        MAX_DISPATCH_LATENCY = 30 SECONDS ,
        MAX_EVENT_SIZE = 0 KB ,
        MEMORY_PARTITION_MODE = NONE ,
        TRACK_CAUSALITY = OFF ,
        STARTUP_STATE = OFF );
GO

不匹配的事件和操作信息會記錄在內存目標中,可以通過GUI或TSQL查詢目標,但是當事件會話結束后,數據就被清空。

總結:

本節是目前系列的最后一節,若后續作者有更新將繼續翻譯。本節介紹了關于事件、操作、謂詞和目標及擴展事件的基本使用情況。使用不同的目標,搭配不同的謂詞、事件,可以對SQL Server進行深入的、輕量級的監控分析,也可以理解SQL Server的工作機制。 微軟通過對擴展事件進行重寫,使其更加低開銷、高度可擴展。 在配置會話事件是,注意下面幾點:如非必要不要添加事件。不要添加不必要的操作。在謂詞中使用短路邏輯最小化數據收集的開銷,特別在收集大量事件,并且事件被頻繁觸發,或采集的事件本身就是高開銷的。根據目的選擇必要的目標,并且考慮存儲時效。 最后,記住擴展事件并不是“僅僅”為了替代Trace。它既覆蓋了絕大部分Trace功能,也提供了偵測問題的新方向。
Tag標簽: 進階   事件   概念  
  • 專題推薦

About IT165 - 廣告服務 - 隱私聲明 - 版權申明 - 免責條款 - 網站地圖 - 網友投稿 - 聯系方式
本站內容來自于互聯網,僅供用于網絡技術學習,學習中請遵循相關法律法規
香港最快开奖现场直播结果