触发器内try...catch无法捕获死锁(1205错误),因sql server在语句执行中途终止牺牲品事务,catch块根本不会运行;真正有效的死锁响应方式是使用system_health扩展事件或sql server agent监听deadlock_graph事件。

不能在触发器里捕获死锁,也无法靠它发警报——1205错误在触发器内部根本进不了 TRY...CATCH 块。
为什么触发器内 TRY...CATCH 对死锁无效
SQL Server 在检测到死锁时,会立即终止牺牲品事务,并抛出错误号 1205。这个过程发生在语句执行中途,**触发器的 CATCH 块根本不会被执行**。你加了 TRY...CATCH 也看不到任何日志,更别提重试或发警报。
常见错误现象:
- 在
AFTER INSERT触发器里写了完整的TRY...CATCH,但应用层仍直接收到Transaction (Process ID XX) was deadlocked - 试图在
CATCH里调用sp_send_dbmail发邮件,结果什么都没发出去——因为CATCH根本没运行
真正能捕获死锁的位置只有两个
死锁不是“异常”,而是 SQL Server 主动中止事务的系统行为。想响应它,必须在事务被中止前就介入,或在中止后从外部感知。
-
system_health扩展事件会自动记录xml_deadlock_report(默认开启,无需配置) - SQL Server Agent 可监听 WMI 事件:
DEADLOCK_GRAPH,并触发作业存档或发通知
这两个机制都绕过了触发器,也不依赖事务是否成功——它们监听的是 SQL Server 内部的死锁信号,而非 T-SQL 执行流。
如果非要“在数据变更时联动处理死锁”,该怎么做
不能靠触发器,但可以靠上游控制 + 异步解耦:
- 把原始 DML(如
INSERT INTO Orders)包装进带重试逻辑的WHILE循环,用TRY...CATCH捕获1205并计数;失败 3 次后,写入一张DeadlockRetryLog表 - 另建一个 SQL Server Agent 作业,每分钟轮询
DeadlockRetryLog,对新记录调用sp_send_dbmail或写入监控平台 - 避免在重试循环里做耗时操作(比如发邮件、查远程 API),否则可能延长阻塞时间,加剧死锁
注意:WAITFOR DELAY '00:00:00.05' 是必须的退避策略,不加会导致重试风暴,让死锁更频繁。
最容易被忽略的一点
很多人花大力气改触发器逻辑,却忘了最基础的:**触发器本身是死锁高发区**。只要它里面做了跨库写入、调用了 EXEC xp_cmdshell、或者更新了被高频争用的表(比如订单状态汇总表),就是在主动制造死锁条件。比起“捕获死锁”,先删掉触发器里所有非必要 I/O 和远程调用,收益大得多。











