是,触发器中执行UPDATE极易引发死锁,因其复用当前事务锁,跨表或非主键更新易导致循环等待;应避免跨表DML、强制索引访问、优先用BEFORE触发器或移至应用层统一锁序。

触发器里执行 UPDATE 会引发死锁吗
会,而且很常见。MySQL 触发器运行在当前事务上下文中,BEFORE 或 AFTER 触发的语句会复用同一事务的锁,如果触发器里再对其他行或表做 UPDATE,就可能和并发事务形成循环等待:A 锁了 t1.id=1 等 t2.id=2,B 锁了 t2.id=2 等 t1.id=1 —— 死锁立即触发。
关键不是“能不能写”,而是“写什么、怎么写”。避免死锁的核心是让所有事务以相同顺序获取锁,并尽可能缩短持有锁的时间。
- 触发器中禁止跨表
UPDATE,尤其不能更新其他业务主表(如从订单表触发器去改用户余额表) - 必须更新关联表时,只允许更新单行、且 WHERE 条件必须命中主键或唯一索引(避免间隙锁扩大范围)
- 不要在触发器里调用存储过程,除非该过程明确不涉及 DML 或已做锁顺序约束
为什么 AFTER INSERT 触发器比 BEFORE 更容易死锁
AFTER INSERT 触发器执行时,新记录已插入并加了行级 X 锁,但事务尚未提交;此时若触发器再去 UPDATE 同一表的其他行,就会在已有锁基础上申请新锁 —— 锁粒度叠加、等待链拉长。而 BEFORE 触发器虽可修改 NEW 值,但不会额外加锁(除非显式 SELECT ... FOR UPDATE)。
更危险的是:很多业务用 AFTER INSERT 去维护统计表(如计数器),而统计表本身又被高频 UPDATE,极易成为死锁热点。
- 统计类逻辑尽量移出触发器,改用应用层异步更新或定时任务
- 非得用触发器维护汇总字段时,优先选
BEFORE INSERT/UPDATE,直接计算并赋值给NEW.count_field - 如果必须用
AFTER,确保被更新的目标表只有极低并发写入(例如日志归档表),且更新语句带WHERE id = ?主键条件
如何验证触发器是否引入了隐式锁升级
MySQL 不会把触发器里的语句单独标记为“触发器操作”,它只会按实际执行的 SQL 申请锁。一个常见陷阱是:触发器中写了 SELECT * FROM t2 WHERE status = 'pending',表面看只是查询,但如果 status 没索引,就会触发全表扫描 + 临键锁(Next-Key Lock),把整张表锁住 —— 这比显式 UPDATE 更隐蔽、更伤并发。
查锁行为最直接的方式是复现场景后立刻执行:
SHOW ENGINE INNODB STATUS\G
重点关注 TRANSACTIONS 部分的 lock_mode X locks rec but not gap 和 lock_trx_id 对应关系,再结合 LOCK WAIT 判断是否由触发器 SQL 引起。
- 所有触发器内的
SELECT必须走主键或覆盖索引,禁用无索引条件的范围查询 - 避免在触发器中使用子查询(尤其是
IN (SELECT ...)),MySQL 可能将其重写为临时表 + 全表扫描 - 上线前用
EXPLAIN检查触发器中每条 SQL 的执行计划,确认type是const、eq_ref或ref,绝不能出现ALL或index
替代方案:用应用层事务控制锁顺序更可靠
触发器本质是黑盒逻辑,开发、DBA、监控都难感知其锁行为。真正可控的做法,是把原本放在触发器里的数据一致性逻辑,提到应用层事务中,显式控制 DML 顺序。
比如“下单成功后扣减库存”,原来用 AFTER INSERT ON orders 触发器去 UPDATE products SET stock = stock - 1,现在改为:
BEGIN; INSERT INTO orders (...) VALUES (...); UPDATE products SET stock = stock - 1 WHERE id = ? AND stock >= 1; -- 检查 ROW_COUNT() == 1,否则回滚 COMMIT;
这样你能保证:所有业务都先锁订单行、再锁商品行,锁顺序统一,且事务边界清晰。
- 触发器适合做纯审计、日志、字段派生(如自动生成 code 字段),不适合做跨资源状态变更
- 如果业务已重度依赖触发器,至少把多表更新拆成独立事务(用
INSERT DELAYED或消息队列解耦),而不是塞进同一个触发器 - 任何用到触发器的表,必须在注释里写明“该表含触发器,禁止在应用层绕过 ORM 直接 INSERT/UPDATE”
最麻烦的不是写错触发器,而是别人不知道它存在 —— 尤其是那个没加索引的 SELECT,可能在线上扛住三个月才爆发一次死锁,然后花两天才发现是触发器里一行查询惹的祸。


















