死锁日志中触发器参与的判断依据有三:一是TRANSACTION段显示tables in use/locked大于1,多出的表常为触发器引入;二是HOLDS与WAITING锁表不一致,如主锁t_order却等待t_user,提示存在AFTER INSERT触发器;三是executionStack中出现trg_*类触发器名,是唯一确凿证据。

怎么看死锁日志里有没有触发器参与
直接执行 SHOW ENGINE INNODB STATUS\G,跳转到 LATEST DETECTED DEADLOCK 区块,重点盯三处:
-
TRANSACTION段中是否出现mysql tables in use 2, locked 2—— 数字大于 1 表明至少涉及两张表,而主 SQL 通常只操作一张业务表,多出来的那张大概率是触发器带进来的(比如t_audit、t_log) -
HOLDS THE LOCK(S)和WAITING FOR THIS LOCK TO BE GRANTED中的表名是否不一致:主事务锁t_order,等待锁却落在t_user或t_summary上,立刻去查t_order表上是否存在AFTER INSERT触发器 - 堆栈顺序是否呈现“INSERT → UPDATE”紧耦合:例如
INSERT INTO t_order后紧跟UPDATE t_log SET status = 'done' WHERE order_id = ?,这就是典型触发器执行痕迹,不是应用层发出的
怎么用动态视图反推触发器是否卷入阻塞
SQL Server 中 sys.dm_tran_locks 不会标出“这是触发器干的”,但能通过交叉比对暴露异常路径:
- 查
sys.dm_tran_locks中request_status = 'GRANT'和'WAIT'的会话,看它们共同锁住的表:如果t_order是主 SQL 显式访问,而t_config只在某个触发器定义里出现过,且同时出现在两个会话的锁列表中,基本坐实触发器参与 - 用
sys.dm_exec_requests查blocking_session_id非 0 的请求,再通过sql_handle反查原始语句:SELECT text FROM sys.dm_exec_sql_text(sql_handle)—— 若看到的是INSERT INTO t_order,但该会话却持有t_user表的排它锁,就得立刻检查触发器逻辑 - 查
sys.dm_exec_trigger_stats,重点关注total_elapsed_time / execution_count是否突增(如从 0.5ms 跳到 300ms),若与死锁高发时段重合,说明触发器内部可能卡在未索引查询或 I/O 等待上
为什么 executionStack 是唯一可信线索
触发器没有独立事务 ID,它的锁和语句都合并到主事务上下文中,所以常规锁视图、会话状态无法分离它的行为。真正能确认它是否在场的,只有死锁图 XML 中的 executionStack:
- 打开
xml_deadlock_report,在process-list里找inputbuf是主 SQL(如INSERT INTO orders)的节点,再顺藤摸瓜看它的executionStack子节点 - 如果里面出现类似
trg_after_update_inventory、trg_log_payment这样的名字,就是铁证 - 注意:如果触发器内调用了存储过程,
executionStack可能只显示到外层触发器名,得配合sys.dm_exec_sql_text手动反查对应sql_handle的完整文本,否则容易漏掉嵌套 DML
哪些现象一出现就该怀疑触发器
不用等死锁发生,日常监控就能提前预警:
- 某张日志表(如
t_audit、t_sync_log)频繁出现在sys.dm_tran_locks的等待列表中,且总是和业务主表(如t_order)成对出现 -
SHOW ENGINE INNODB STATUS里反复出现lock_mode X locks gap before rec,但主 SQL 并没做范围查询 —— 很可能是触发器里的SELECT ... FOR UPDATE或非唯一索引UPDATE引发的间隙锁 - 同一张表上的
INSERT操作并发量不高,却持续报ERROR 1213 (40001),且EXPLAIN显示主 SQL 本身走主键,排除了自身锁扩大问题
executionStack 里留下名字,在锁视图里混进主事务的 session_id 下——排查时最容易忽略的就是这个“不可见性”。

















