SHOW ENGINE INNODB STATUS 看不到触发器内SQL,因其仅记录顶层语句,触发器操作被归入父事务且不单独显示锁来源,需结合死锁日志、performance_schema和触发器定义交叉验证。

触发器引发的隐式锁无法被 SHOW ENGINE INNODB STATUS 直接暴露,必须靠交叉验证才能定位——它不报错、不显式加锁、却真实阻塞后续 UPDATE。
为什么 SHOW ENGINE INNODB STATUS 看不到触发器内的 SQL
MySQL 的 SHOW ENGINE INNODB STATUS 只记录客户端发起的顶层语句(如 INSERT INTO t_order),而触发器内执行的 UPDATE t_log SET status = 'done' WHERE ref_id = NEW.id 完全不会出现在 LATEST DETECTED DEADLOCK 或 TRANSACTIONS 段中。InnoDB 把它视作父事务的一部分,锁信息统一归到外层事务 ID 下,日志里只显示“持有锁的表”和“等待锁的表”,不体现谁下的锁。
常见错误现象:
- 应用报
Deadlock found when trying to get lock,但死锁日志里两个事务都只显示主表操作,看不出冲突点 -
INNODB_TRX中某事务trx_state是LOCK WAIT,但trx_query显示的是 INSERT,而实际卡住的是触发器里对另一张表的 UPDATE - 用
performance_schema.data_lock_waits查到等待链,BLOCKING_ENGINE_TRANSACTION_ID对应的事务在INNODB_TRX里查不到trx_query—— 因为它早已执行完 INSERT,正卡在触发器逻辑里
如何确认死锁或阻塞来自触发器
关键看三处线索是否形成闭环:
- 死锁日志中,两个事务的
HOLDS THE LOCK(S)和WAITING FOR THIS LOCK TO BE GRANTED涉及的表,是否包含触发器目标表(例如:t_order触发器更新t_audit,但日志里t_audit同时出现在 HOLD 和 WAIT 两侧) - 应用日志中,对应时间点是否有批量写入(如
INSERT INTO t_order VALUES (...),(...),(...)),且这些操作会激活多个触发器(一次插入触发 3 个不同表的 UPDATE) -
SHOW CREATE TRIGGER trigger_name显示其内部含:UPDATE/DELETE、范围条件(WHERE created_at > DATE_SUB(NOW(), INTERVAL 1 DAY))、SELECT ... FOR UPDATE,或对无索引字段的更新(WHERE module = 'payment',而module无索引)
禁用触发器前必须验证的三个动作
直接 DROP TRIGGER 可能导致业务数据不一致。实操中应先做最小干预验证:
- 临时关闭触发器所在表的自动提交:
SET autocommit = 0,然后手动执行一条会触发它的 INSERT,再用SELECT * FROM performance_schema.data_locks WHERE OBJECT_NAME = 't_audit'确认锁是否真由该触发器产生 - 用
EXPLAIN FORMAT=tree SELECT * FROM t_audit WHERE ref_id = ?(把触发器中的 WHERE 条件单独抽出来)验证是否走索引;若type是ALL或key是NULL,说明触发器内这句 SQL 会锁全表,是高危源 - 检查是否存在级联触发:
SELECT * FROM information_schema.TRIGGERS WHERE EVENT_OBJECT_TABLE = 't_audit',确认t_audit上是否还有其他触发器——禁用原触发器可能让级联链断裂,反而引发更隐蔽的数据异常
禁用复杂逻辑触发器的替代方案
多数情况下,禁用不是终点,而是为了腾出窗口改用更可控的方式。以下方案按风险从低到高排列:
- 把触发器逻辑移到应用层异步任务(如消息队列消费),避免锁在数据库事务内延长;注意补偿机制,防止消息丢失后数据不一致
- 改用存储过程封装逻辑,调用方显式
CALL update_audit_after_order(NEW.id),便于在业务代码中统一控制调用时机和重试 - 若必须保留在 DB 层,将原触发器拆成两阶段:
BEFORE INSERT只做轻量校验,AFTER INSERT改为写入中间表(如t_audit_pending),再由定时 Job 批量处理——把长事务锁拆成短事务 + 异步延迟
真正难的从来不是“怎么关掉触发器”,而是关掉之后,那条本该自动跑的 UPDATE 逻辑,有没有被等价、可监控、可回溯的方式承接住。否则,你只是把死锁从数据库日志里藏到了业务数据的毛刺里。











