触发器会显著增加锁等待,因其在事务内执行DML操作,导致隐式锁扩张、锁范围变大、持有时间延长,进而引发Lock wait timeout exceeded;可通过查询INNODB_TRX和SHOW ENGINE INNODB STATUS定位,推荐用异步+幂等或INSERT ... ON DUPLICATE KEY UPDATE替代。

触发器确实会显著增加锁等待,尤其在高并发写入场景
是的,MySQL 触发器本身不直接“加锁”,但它在事务内执行时,会把触发逻辑里的 DML 操作(如 UPDATE、INSERT、SELECT ... FOR UPDATE)合并进主事务的锁生命周期。这意味着:原本一个简单的 INSERT 可能只持有一行 X 锁,加上触发器后,可能额外请求 1~3 次行锁甚至间隙锁,锁持有时间变长、锁范围变大,自然抬高了其他事务的等待概率。
为什么触发器让 Lock wait timeout exceeded 更常见
根本原因在于“隐式锁扩张”和“锁顺序不可控”。比如一个 AFTER INSERT 触发器去更新汇总表 stats_counter:
- 若
stats_counter.group_id没有索引,UPDATE ... WHERE group_id = ?会升级为全表扫描 + 行锁,甚至 GAP LOCK,大幅延长锁持有时间 - 多个事务并发插入不同记录但命中同一
group_id,就会排队争抢同一行——后到的事务等前一个事务提交,超时即报Lock wait timeout exceeded - 触发器中调用子查询或函数,若该查询未走索引,也会扩大扫描范围,间接拉长锁等待链
怎么快速验证触发器是不是罪魁祸首
别靠猜,直接查 INFORMATION_SCHEMA.INNODB_TRX 和死锁日志:
- 运行
SELECT * FROM information_schema.INNODB_TRX\G,看TRX_QUERY字段里是否频繁出现触发器中写的语句(如UPDATE stats_counter SET cnt = cnt + 1 WHERE group_id = 42) - 执行
SHOW ENGINE INNODB STATUS\G,翻到LATEST DETECTED DEADLOCK或TRANSACTIONS段,重点找 “holds the lock(s)” 和 “waiting for this lock to be granted” 对应的 SQL —— 如果全是触发器内部的UPDATE,基本坐实 - 临时禁用触发器(
ALTER TABLE t DISABLE TRIGGER trigger_name)压测对比锁等待次数,效果立竿见影
比“修触发器”更稳的替代方案
很多团队卡在“改触发器逻辑”上反复折腾,其实问题根源是把实时强一致性逻辑塞进了数据库层。更可持续的做法是解耦:
- 用应用层异步+幂等处理:插入主表后,发 MQ 消息给计数服务,由它用
Redis INCR原子累加,再按需批量落库 - 改用
INSERT ... ON DUPLICATE KEY UPDATE替代触发器中的UPDATE,但必须确保冲突字段(如group_id)有唯一索引,否则仍可能触发间隙锁竞争 - 如果必须保留触发器,至少把所有涉及写操作的触发逻辑收归单张汇总表,并强制按
group_idASC 排序更新(避免交叉锁顺序),同时加WHERE条件限制影响行数
真正难的不是写对触发器语法,而是意识到:当锁等待开始上升,往往说明你正在用最重的机制(事务内同步锁)解决本可用轻量手段(缓存+异步)化解的问题。










