触发器引发的锁阻塞不会出现在show engine innodb status的trx_query中,因其被视作父事务一部分;需通过innodb_trx、data_lock_waits及死锁日志三处线索闭环验证:lock wait事务无trx_query、blocking_id对应事务已执行完主语句但卡在触发器逻辑、且同一非主表同时出现在holds和waiting两侧。

触发器引发的锁阻塞根本不会出现在 SHOW ENGINE INNODB STATUS 的事务 SQL 字段里——它被完全吞进父事务,只留锁表名,不露执行路径。你看到的是“卡在 INSERT”,实际堵点是触发器里一条没走索引的 UPDATE t_log。
怎么确认阻塞真来自触发器?
别信 trx_query 显示的内容,它只报顶层语句。要闭环验证三处线索:
- 查
INNODB_TRX中状态为LOCK WAIT的事务,记下TRX_ID - 用该
TRX_ID去performance_schema.data_lock_waits查等待链,找到对应的BLOCKING_ENGINE_TRANSACTION_ID - 再查这个 blocking ID 在
INNODB_TRX里是否没有trx_query(说明它早已执行完主语句,正卡在触发器逻辑中) - 同时核对死锁日志中,
HOLDS THE LOCK(S)和WAITING FOR THIS LOCK TO BE GRANTED是否都含同一张非主表(比如t_order插入触发t_audit更新,而t_audit同时出现在 HOLD 和 WAIT 两侧)
为什么 EXPLAIN 看不出触发器里的锁风险?
EXPLAIN 模拟的是独立语句,但触发器内 DML 继承主事务的隔离级别和锁行为。RR 模式下,哪怕 WHERE status = 'done' 走了索引,InnoDB 仍可能加间隙锁;如果字段没索引,type 显示 ALL 或 index,那就不是慢,是直接锁全表。
- 必须用真实参数重放:比如触发器里是
UPDATE t_stock SET qty = qty - 1 WHERE sku = NEW.sku,就执行EXPLAIN SELECT * FROM t_stock WHERE sku = 'ABC123' FOR UPDATE - 重点盯
key是否为空、rows是否远超实际影响行数——这是间隙锁乱加的信号 -
SHOW CREATE TRIGGER trigger_name看有没有SELECT ... FOR UPDATE、范围条件或无索引字段更新
禁用前先做最小验证,别直接 DROP
直接删触发器可能让业务数据错位(比如审计日志断更、状态同步失效)。先用三步低成本验证:
- 临时关自动提交:
SET autocommit = 0,手动执行一条会触发它的INSERT,再立刻查performance_schema.data_locks看目标表是否真被锁住 - 往专用调试表
trigger_debug插记录,包含NOW()、USER()、NEW.id,确认它确实被调用且时间吻合阻塞点 - 把触发器改成
BEFORE INSERT SET NEW.invalid_col = 'x'(故意设不存在字段),看是否报错Unknown column—— 报了,说明它真被激活了
真正难的不是建索引或调隔离级别,而是接受一个事实:触发器不是自动省事的黑盒,它是主事务的延伸。它写的每一行、锁的每一个间隙,都算在你那条 INSERT 的账上。排查时盯着 data_lock_waits 和死锁日志交叉比对,比反复刷 SHOW PROCESSLIST 有用十倍。











