默认情况下,我可以从SQL Server获取哪些事件信息?


60

我经常在人们想知道某件事是否发生,何时发生或由谁执行操作的问题上遇到疑问。在很多情况下,SQL Server不会单独跟踪此信息。例如:

  • 谁最后执行存储过程dbo.MyProcedure
  • 谁更新salarydbo.Employees表格中的列?
  • 谁最后dbo.Orders从Management Studio 查询表格?

但是默认情况下,SQL Server 临时跟踪其他一些事件,这些事件可以本地回答有关的问题,例如:

  • 上一次在AdventureWorks数据库中发生自动增长是什么时候?
  • 谁删除了dbo.EmployeeAuditData表格,何时删除?
  • 今天发生了多少与内存相关的错误?

我如何获得此信息,以及该信息可以使用多长时间?

Answers:


65

默认情况下,SQL Server会为您跟踪很多有价值的信息。从SQL Server 2005开始,已经有一个“默认跟踪”在后台运行,并且从SQL Server 2008开始,已经有一个自动运行的扩展事件会话,称为system_health

您还可以从SQL Server错误日志,SQL Server代理日志,Windows事件日志以及诸如SQL Server审核管理数据仓库事件通知DML触发器DDL触发器SCOM /系统中心之类的其他日志中找到某些信息。,您自己的服务器端跟踪或扩展事件会话,或第三方监视解决方案(例如由我的雇主SQL Sentry制造的解决方案)。您还可以选择启用所谓的“黑盒跟踪”以协助进行故障排除

但是对于本篇文章,我将集中讨论通常在大多数地方启用的事物:默认跟踪,扩展事件会话和错误日志。

默认跟踪

默认跟踪通常在大多数系统上运行,除非您使用禁用了它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;

您可以通过加入以sys.trace_columns查看哪些事件与哪些数据一起来获得更多细节,但是由于我可以实际查询特定事件的跟踪数据时看到的内容,因此我现在将跳过。这些是我的系统上可用的事件(您应该在自己的系统上运行查询以确保它们匹配,尽管通过SQL Server 2019 CTP 2.4仍然是同一组事件):

EventID  Event_Description
-------  ----------------------------------------------
18       Audit Server Starts And Stops
20       Audit Login Failed
22       ErrorLog
46       Object:Created
47       Object:Deleted
55       Hash Warning
69       Sort Warnings
79       Missing Column Statistics
80       Missing Join Predicate
81       Server Memory Change
92       Data File Auto Grow
93       Log File Auto Grow
94       Data File Auto Shrink
95       Log File Auto Shrink
102      Audit Database Scope GDR Event
103      Audit Schema Object GDR Event
104      Audit Addlogin Event
105      Audit Login GDR Event
106      Audit Login Change Property Event
108      Audit Add Login to Server Role Event
109      Audit Add DB User Event
110      Audit Add Member to DB Role Event
111      Audit Add Role Event
115      Audit Backup/Restore Event
116      Audit DBCC Event
117      Audit Change Audit Event
152      Audit Change Database Owner
153      Audit Schema Object Take Ownership Event
155      FT:Crawl Started
156      FT:Crawl Stopped
164      Object:Altered
167      Database Mirroring State Change
175      Audit Server Alter Trace Event
218      Plan Guide Unsuccessful

请注意,默认跟踪使用过渡文件,因此您可用的数据将仅返回至现在为止-可用数据的日期范围取决于捕获到的上述事件的数量以及发生的频率。如果要确保保留更长的历史记录,可以设置一个作业,以定期存档掉与跟踪关联的当前非活动文件。

例子

在这个问题中,我问了几个发现的问题。以下是用于从默认跟踪中提取特定信息的查询示例。

问题:上一次在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;

问题:谁删除了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;

这里有一个复杂的情况,这是一个非常极端的情况,但是无论如何还是认为谨慎。如果您使用多个模式,并且在多个模式中可能具有相同的对象名称,则您将无法分辨出这是哪一个(除非其对应对象仍然存在)。有一个外部情况,UserA可能已删除SchemaB.Tablename,而UserB可能已删除SchemaA.Tablename。默认跟踪不会跟踪对象的架构(也不会捕获TextData此事件),并且ObjectID包含在跟踪中对于直接匹配没有用(因为该对象已删除并且不再存在)。在这种情况下,在输出中包括该列可能有助于交叉引用仍然存在的具有相同名称的表的任何副本,但是如果系统处于如此混乱的状态(或者如果所有此类副本都已删除)仍然可能不是可靠的方法来猜测谁删除了表的哪个副本。

扩展活动

支持SQL Server 2008:system_health会话(SQLCSS博客)中,以下是可以从system_healthSQL Server 2008和2008 R2中的会话中筛选的数据列表:

  • 遇到严重性> = 20的错误的任何会话的sql_text和session_id
  • 遇到“内存”类型的错误(例如17803、701等)的任何会话的sql_text和session_id(我们添加了此代码是因为并非所有内存错误的严重性> = 20)
  • 任何“非屈服”问题的记录(您有时在ERRORLOG中看到这些消息为Msg 17883)
  • 检测到的任何死锁
  • 在锁存器(或其他有趣的资源)上等待超过15秒的任何会话的callstack,sql_text和session_id
  • 调用等待锁定时间超过30秒的任何会话的callstack,sql_text和session_id
  • 任何已等待较长时间的“外部”等待或“抢先等待”会话的会话的callstack,sql_text和session_id。

从“ 使用system_health事件会话(MSDN)”中,该列表在SQL Server 2012中有所扩展(对于SQL Server 2014则保持不变):

  • 遇到严重性> = 20的错误的任何会话的sql_text和session_id。
  • 遇到与内存相关的错误的任何会话的sql_text和session_id。错误包括17803、701、802、8645、8651、8657和8902。
  • 任何非收益调度程序问题的记录。(这些内容在SQL Server错误日志中显示为错误17883。)
  • 检测到的任何死锁。
  • 在锁存器(或其他有趣的资源)上等待的所有会话的调用堆栈,sql_text和session_id> 15秒。
  • 锁定等待时间超过30秒的任何会话的调用堆栈,sql_text和session_id。
  • 长时间等待抢先等待的任何会话的调用堆栈,sql_text和session_id。持续时间因等待类型而异。抢先等待是SQL Server等待外部API调用的地方。
  • 用于CLR分配和虚拟分配失败的callstack和session_id。
  • 内存代理,调度程序监视器,内存节点OOM,安全性和连接性的ring_buffer事件。
  • 系统组件来自sp_server_diagnostics。
  • 由scheduler_monitor_system_health_ring_buffer_recorded收集的实例运行状况。
  • CLR分配失败。
  • 使用Connectivity_ring_buffer_recorded的连接错误。
  • 使用security_error_ring_buffer_recorded的安全性错误。

在SQL Server 2016中,还捕获了两个事件:

  • 使用KILL命令杀死进程时。
  • 开始SQL Server关闭时。

(文档尚未更新,但是我在博客中介绍了如何发现这些以及其他更改。)

为了获得适用于您特定版本的更神秘的配置,您始终可以直接运行以下查询,但是您必须解释名称并解析谓词以匹配上面的更自然的语言列表:

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;

如果您使用的是可用性组,那么还会有两个新的会话正在运行:AlwaysOn_failoverAlwaysOn_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;

这些事件会话使用环形缓冲区目标来存储数据,因此-如缓冲池和计划缓存-较旧的事件将被淘汰,因此您不一定能够从所需的日期范围提取事件。

在这个问题上,我提出了一个虚拟的问题:

今天发生了多少与内存相关的错误?

这是一个示例查询(可能效率不高),可以从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;

(此示例宽松地借鉴了Amit Banerjee在system_health会议上的介绍性博客文章。)

有关扩展事件的更多信息(包括许多示例,可在其中查询特定数据),请参阅乔纳森·凯海耶斯撰写的由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';

在最近的回答中还介绍了其他一些细节在toadworld官方文档中也有一些很好的背景信息。

默认情况下,错误日志会跟踪一组“错误”,并且可以使重要信息更快地消失-这是每条成功的备份消息。通过启用跟踪标志3226,可以防止这些错误使错误日志充满噪声。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.