死锁图由system_health扩展事件自动捕获,需轮询ring_buffer提取xml_deadlock_report事件,解析时须声明XMLNAMESPACES并用XQuery提取结构化字段,配合SHA2_256哈希去重写入监控表,且轮询应通过SQL Agent Job实现。

死锁图捕获依赖 system_health 扩展事件,不是存储过程本身能直接“生成”的
SQL Server 不允许在存储过程中主动触发死锁图生成,死锁图是系统在真实死锁发生时由 system_health 扩展事件会话自动捕获的 XML 数据。你无法在 TRY...CATCH 里用 ERROR_MESSAGE() 拿到死锁图——它根本不在错误消息文本里。真正可行路径是:定期轮询 system_health 的环形缓冲区,提取新出现的死锁事件,解析后写入监控表。
-
system_health默认启用,死锁事件名为xml_deadlock_report,数据存于ring_buffer目标中 - 必须用
sys.dm_xe_session_targets+sys.dm_xe_sessions关联查询,不能只查 DMV 视图(如sys.dm_exec_requests) - XML 解析需用
.value()提取关键字段,例如(/event/@timestamp)[1]、(/event/data/value/deadlock)[1] - 轮询间隔建议 ≥ 30 秒,太频繁会加重系统负担;首次运行需记录上一次最大
timestamp,避免重复入库
写入监控表前必须预处理 XML,否则直接插入会失败或丢失结构
死锁图 XML 体积大(常超 2MB),且含特殊字符和命名空间,不能原样存为 NVARCHAR(MAX) 后再解析——后续查询效率极低,且 .query() 在大 XML 上性能陡降。正确做法是:在 INSERT 前就用 XQuery 提取核心指标,只存结构化字段 + 原始 XML 的哈希值用于去重。
- 必提字段:
(/event/@timestamp)[1]→ 转为DATETIME2;(/deadlock/victim-list/victimProcess/@id)[1]→ 死锁牺牲者 SPID - 用
HASHBYTES('SHA2_256', CAST(@xml AS NVARCHAR(MAX)))生成指纹,防止同一死锁被多次抓取 - 原始 XML 存为
XML类型(非NVARCHAR),确保后续可用.nodes()遍历参与进程 - 注意命名空间声明:
WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2008/09/events')必须显式指定,否则.value()返回 NULL
监控表设计要支持快速归因,不能只存“一张图”
只存死锁图 XML 等于没监控。真正有用的表结构必须包含可索引、可关联、可聚合的字段,否则运维查问题时还得手动开 XML 一点一点翻。
- 至少包含:
capture_time DATETIME2(事件时间,非插入时间)、victim_spid INT、process_count SMALLINT(参与进程数)、resource_db NVARCHAR(128)(首个被争用数据库)、hash BINARY(32)(SHA2_256 指纹) -
hash字段建唯一索引,配合轮询逻辑中的WHERE hash NOT IN (SELECT hash FROM ...)实现幂等写入 - 避免在监控表中存
TEXT或巨型XML字段作为查询条件——把高频过滤字段(如capture_time、victim_spid)单独建索引 - 如果需定位具体 SQL,得从 XML 中解析
/deadlock/process/@inputbuf,但注意该字段可能被截断,不可全信
轮询作业必须用 SQL Agent Job 而非存储过程定时调用
靠应用层调用存储过程实现“定时”不可靠:连接中断、应用重启、调度延迟都会导致漏采。SQL Server Agent 是唯一能保证周期性、上下文隔离、失败可告警的机制。
- Job 步骤类型选
T-SQL script,不要用 PowerShell 或 CmdExec——增加故障面 - 脚本开头加
SET LOCK_TIMEOUT 5000;,防止轮询查询自身被阻塞 - 捕获逻辑外层套
TRY...CATCH,对INSERT失败做简单日志(写入msdb.dbo.sysjobhistory或自建日志表),不抛错中断 Job - 不要在 Job 中执行
DBCC TRACEON(1222, -1)—— 这是过时方案,输出到 errorlog 不便于解析,且影响全局性能
实际部署时最易忽略的是 XML 命名空间和哈希去重逻辑。少写一行 WITH XMLNAMESPACES,整条解析就静默返回 NULL;不校验哈希,同一死锁在高并发场景下可能被写入十几次——监控表迅速膨胀,反而掩盖真实问题频次。











