Aar*_*and 61 sql-server extended-events
我经常看到这样的问题,人们想知道某件事是否发生、何时发生或是谁执行的。在很多情况下,SQL Server 不会自行跟踪此信息。例如:
dbo.MyProcedure?salary了dbo.Employees表中的列?dbo.Orders从 Management Studio查询了表?但是,SQL Server默认会临时跟踪其他几个事件,并且可以在本机回答有关问题,例如:
dbo.EmployeeAuditData表,何时删除?我如何获得这些信息,以及它可以保持多久?
Aar*_*and 67
默认情况下,SQL Server 会为您跟踪很多有价值的信息。自 SQL Server 2005 以来,有一个在后台运行的“默认跟踪”,自 SQL Server 2008 以来,有一个扩展事件会话自动运行,称为system_health.
您还可以从 SQL Server 错误日志、SQL Server 代理日志、Windows 事件日志以及其他日志记录中找到某些信息,例如SQL Server 审计、管理数据仓库、事件通知、DML 触发器、DDL 触发器、SCOM/系统中心、您自己的服务器端跟踪或扩展事件会话,或第三方监控解决方案(例如由SentryOne制作的解决方案)。您还可以选择启用所谓的“黑盒跟踪”以帮助排除故障。
但是对于这篇文章,我将把范围集中在通常在任何地方都启用的东西上:默认跟踪、扩展事件会话和错误日志。
默认跟踪通常在大多数系统上运行,除非您使用sp_configure. 只要启用它,它就可以成为宝贵信息的丰富来源。下面列出了捕获的跟踪事件:
DECLARE @TraceID INT;
SELECT @TraceID = id FROM sys.traces WHERE is_default = 1;
SELECT t.EventID, e.name as Event_Description
FROM sys.fn_trace_geteventinfo(@TraceID) t
JOIN sys.trace_events e ON t.eventID = e.trace_event_id
GROUP BY t.EventID, e.name;
Run Code Online (Sandbox Code Playgroud)
您可以通过加入以sys.trace_columns查看哪些事件带有哪些数据来获得更多详细信息,但我现在将跳过它,因为当您实际查询特定事件的跟踪数据时,您可以看到您拥有的内容。这些是我的系统上可用的事件(你应该在你的系统上运行查询以确保它们匹配,尽管这仍然是通过 SQL Server 2019 CTP 2.4 的相同事件集):
DECLARE @TraceID INT;
SELECT @TraceID = id FROM sys.traces WHERE is_default = 1;
SELECT t.EventID, e.name as Event_Description
FROM sys.fn_trace_geteventinfo(@TraceID) t
JOIN sys.trace_events e ON t.eventID = e.trace_event_id
GROUP BY t.EventID, e.name;
Run Code Online (Sandbox Code Playgroud)
请注意,默认跟踪使用翻转文件,因此您可用的数据只能回溯到目前为止 - 可用数据的日期范围取决于捕获上述事件的数量和频率。如果要确保保留更长的历史记录,可以设置一个作业,定期将与跟踪关联的当前非活动文件归档。
例子
在这个问题中,我问了几个我发现的问题。以下是用于从默认跟踪中提取特定信息的示例查询。
问题:上一次 AdventureWorks 数据库中的自动增长是什么时候?用了多长时间?
此查询将拉取 AdventureWorks 数据库中的所有 AutoGrow 事件,包括仍在默认跟踪日志文件中的日志和数据文件:
DECLARE @path NVARCHAR(260);
SELECT
@path = REVERSE(SUBSTRING(REVERSE([path]),
CHARINDEX(CHAR(92), REVERSE([path])), 260)) + N'log.trc'
FROM sys.traces
WHERE is_default = 1;
SELECT
DatabaseName,
[FileName],
SPID,
Duration,
StartTime,
EndTime,
FileType = CASE EventClass WHEN 92 THEN 'Data' ELSE 'Log' END
FROM sys.fn_trace_gettable(@path, DEFAULT)
WHERE EventClass IN (92,93)
AND DatabaseName = N'AdventureWorks'
ORDER BY StartTime DESC;
Run Code Online (Sandbox Code Playgroud)
问题:谁删除了 dbo.EmployeeAuditData 表,何时删除?
这将返回DROP名为 的对象的任何事件EmployeeAuditData。如果你想确保它只能检测DROP事件表,您可以添加过滤器:ObjectType = 8277(的完整列表记录在这里)。如果要将搜索空间限制为特定数据库,可以添加过滤器:DatabaseName = N'db_name'。
DECLARE @path NVARCHAR(260);
SELECT
@path = REVERSE(SUBSTRING(REVERSE([path]),
CHARINDEX(CHAR(92), REVERSE([path])), 260)) + N'log.trc'
FROM sys.traces
WHERE is_default = 1;
SELECT
LoginName,
HostName,
StartTime,
ObjectName,
TextData
FROM sys.fn_trace_gettable(@path, DEFAULT)
WHERE EventClass = 47 -- Object:Deleted
AND EventSubClass = 1
AND ObjectName = N'EmployeeAuditData'
ORDER BY StartTime DESC;
Run Code Online (Sandbox Code Playgroud)
这里有一个复杂的问题,这是非常极端的情况,但无论如何还是要谨慎提及。如果您使用多个模式并且可能在多个模式中具有相同的对象名称,您将无法分辨这是哪一个(除非它的对应项仍然存在)。有一种外部情况,即 UserA 可能已删除 SchemaB.Tablename,而 UserB 可能已删除 SchemaA.Tablename。默认跟踪不跟踪对象的架构(也不捕获TextData此事件),并且ObjectID跟踪中包含的内容对于直接匹配没有用(因为对象已删除并且不再存在)。在这种情况下,在输出中包含该列可能有助于交叉引用任何仍然存在的同名表副本,但如果系统如此混乱(或者如果所有这些副本都已被删除)仍然可能不是猜测表的哪个副本被谁删除的可靠方法。
以下是您可以从system_healthSQL Server 2008 和 2008 R2 中的会话中提取的数据列表(该列表在现代版本中更完整):
从使用 system_health 事件会话 (MSDN),该列表在 SQL Server 2012 中有所扩展(并且对于 SQL Server 2014 保持不变):
在 SQL Server 2016 中,捕获了另外两个事件:
KILL命令杀死进程时。(文档尚未更新,但我在博客中介绍了我如何发现这些和其他更改。)
要获得适用于您的特定版本的更神秘的配置,您始终可以直接运行以下查询,但您必须解释名称并解析谓词以匹配上面更自然的语言列表:
SELECT e.package, e.event_id, e.name, e.predicate
FROM sys.server_event_session_events AS e
INNER JOIN sys.server_event_sessions AS s
ON e.event_session_id = s.event_session_id
WHERE s.name = N'system_health'
ORDER BY e.package, e.name;
Run Code Online (Sandbox Code Playgroud)
如果您使用的是可用性组,还会发现正在运行的两个新会话:AlwaysOn_failover和AlwaysOn_health. 您可以通过以下查询查看他们收集的数据:
SELECT s.name, e.package, e.event_id, e.name, e.predicate
FROM sys.server_event_session_events AS e
INNER JOIN sys.server_event_sessions AS s
ON e.event_session_id = s.event_session_id
WHERE s.name LIKE N'AlwaysOn[_]%'
ORDER BY s.name, e.package, e.name;
Run Code Online (Sandbox Code Playgroud)
这些事件会话使用环形缓冲区目标来存储数据,因此 - 与缓冲池和计划缓存一样 - 较旧的事件将被逐步淘汰,因此您不一定能够从所需的日期范围中提取事件。
例子
在问题中,我提出了这个虚构的问题:
今天发生了多少与内存相关的错误?
这是一个可以从system_health会话中提取此信息的示例(可能不是很有效)查询:
;WITH src(x) AS
(
SELECT y.query('.')
FROM
(
SELECT x = CONVERT(XML, t.target_data)
FROM sys.dm_xe_sessions AS s
INNER JOIN sys.dm_xe_session_targets AS t
ON s.[address] = t.event_session_address
WHERE s.name = N'system_health'
) AS x
CROSS APPLY x.x.nodes('/RingBufferTarget/event') AS y(y)
)
SELECT
x, ts = CONVERT(DATETIME, NULL), err = CONVERT(INT, NULL)
INTO #blat FROM src;
DELETE #blat WHERE x.value('(/event/@name)[1]', 'varchar(255)') <> 'error_reported';
UPDATE #blat SET ts = x.value('(/event/@timestamp)[1]', 'datetime');
UPDATE #blat SET err = x.value('(/event/data/value)[1]', 'int');
SELECT err, number_of_events = COUNT(*)
FROM #blat
WHERE err IN (17803, 701, 802, 8645, 8651, 8657, 8902)
AND ts >= CONVERT(DATE, CURRENT_TIMESTAMP)
GROUP BY err;
DROP TABLE #blat;
Run Code Online (Sandbox Code Playgroud)
(这个例子从Amit Banerjee 在system_healthsession上的介绍性博客文章中松散地借用了。)
有关扩展事件的更多信息(包括可以查询特定数据的许多示例),请参阅 Jonathan Kehayias 的这个由 31 部分组成的博客系列:
https://www.sqlskills.com/blogs/jonathan/an-xevent-a-day-31-days-of-extended-events/
默认情况下,SQL Server 保留当前加上 6 个最近的错误日志文件(但您可以更改此设置)。那里存储了大量信息,包括启动信息(正在使用多少个内核,是否设置了内存中的锁定页面,身份验证模式等)以及错误和其他严重到足以记录在案(而不会在其他地方捕获)的情况。最近的一个例子是有人在寻找数据库何时脱机。您可以通过扫描文本的最新 7 个错误日志中的每一个来确定这一点Setting database option OFFLINE:
EXEC sys.sp_readerrorlog 0,1,'Setting database option OFFLINE';
EXEC sys.sp_readerrorlog 1,1,'Setting database option OFFLINE';
EXEC sys.sp_readerrorlog 2,1,'Setting database option OFFLINE';
EXEC sys.sp_readerrorlog 3,1,'Setting database option OFFLINE';
EXEC sys.sp_readerrorlog 4,1,'Setting database option OFFLINE';
EXEC sys.sp_readerrorlog 5,1,'Setting database option OFFLINE';
EXEC sys.sp_readerrorlog 6,1,'Setting database option OFFLINE';
Run Code Online (Sandbox Code Playgroud)
我在这个最近的回答中介绍了一些其他细节,在 toadworld和官方文档中也有一些很好的背景信息。
默认情况下,错误日志会跟踪一组“错误” - 并且可以使重要信息更快地从尾部消失 - 是每条成功的备份消息。您可以通过启用跟踪标志 3226来防止这些错误日志填满噪音。