mysql触发器会增加锁等待吗_mysql并发风险说明

来源:这里教程网 时间:2026-02-28 20:46:42 作者:

触发器确实会显著增加锁等待,尤其在高并发写入场景

是的,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_id
ASC 排序更新(避免交叉锁顺序),同时加
WHERE
条件限制影响行数

真正难的不是写对触发器语法,而是意识到:当锁等待开始上升,往往说明你正在用最重的机制(事务内同步锁)解决本可用轻量手段(缓存+异步)化解的问题。

相关推荐