SQL Server 扩展事件(Extented Events)从入门到进阶(4)——扩展事件引擎——基本概念

时间:2023-03-09 02:20:57
SQL Server 扩展事件(Extented Events)从入门到进阶(4)——扩展事件引擎——基本概念

本文属于 SQL Server 扩展事件(Extented Events)从入门到进阶 系列

在第一二节中,我们创建了一些简单的、类似典型SQL Trace的扩展事件会话。在此过程中,介绍了很多扩展事件基础组件,包括事件、谓词、操作和目标。本节,将对扩展事件引擎、架构和基本组件做更加深入的了解。通过这些讲解,可以大概了解到为什么扩展事件相对于SQL Trace来说更加低开销。另外,还会延时如何设计事件会话从而最小化事件收集过程中的不必要开销,即使这些事件会话会很复杂。

事件数据收集生命周期:

扩展事件(Extended Events,XE)中的事件,可以发生在SQL Server进程中的很多地方,每个事件在遇到特定的事件代码时都会触发。比如有些事件是在存储过程开始执行或者编译时触发、遇到死锁时触发、统计信息自动更新时触发、锁申请或释放时触发等。
当正在执行的任务触发事件时,我们希望能够简单地,从任务执行线程直接传入XE引擎。它必须为每个活动事件做以下处理:
  1. 收集事件的基本负载数据。
  2. 评估谓词
  3. 如果谓词为True则收集对应操作。
  4. 直接分派事件数据到所有同步目标或者到中间内存缓冲区等待分派到异步目标。
一旦所有数据被收集完毕。控制流程返回执行线程。前面提到过,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 Server 扩展事件(Extented Events)从入门到进阶(4)——扩展事件引擎——基本概念
如果是在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

本机结果如下:

SQL Server 扩展事件(Extented Events)从入门到进阶(4)——扩展事件引擎——基本概念


事件:

扩展事件,正如其名,其基础组件当然就是“事件(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方式查看,比如第二篇中提到的【事件】页,默认负载是不可定制化的,除了下图的红框那些:

SQL Server 扩展事件(Extented Events)从入门到进阶(4)——扩展事件引擎——基本概念


事件列(也叫数据列)包含了列类型。我们可以把它们作为默认负载的一部分来收集,当然也可以不收集。
虽然扩展事件通常来说开销都比Trace小,但是也还是有一些事件会明显影响性能。比如showplan就是特别需要注意的,比如下图中红框部分明确提醒了:

SQL Server 扩展事件(Extented Events)从入门到进阶(4)——扩展事件引擎——基本概念

即使加上了谓词筛选,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_guid
WHERE ( [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 SERVER
ADD 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秒( ) ,
collect_statement = ( 1 )
ACTION ( sqlserver.client_app_name, sqlserver.database_name )
WHERE ( [logical_reads] >= ( 10000 )
AND [duration] >= ( 1000000 )
) );
GO