会,MySQL触发器本身不直接制造死锁,但极易成为死锁的放大器和触发点;因其在事务中执行并合并锁,涉及写操作时会隐式增加行锁请求,导致锁顺序失控、间隙锁升级及多事务争抢同一行而形成循环等待。
会,MySQL 触发器本身不直接“制造”死锁,但它极易成为死锁的**放大器和触发点**——尤其在高并发、多事务、无序加锁的场景下,一个看似简单的 AFTER INSERT 触发器,可能瞬间把两个事务拖进循环等待。
触发器在事务上下文中执行,它持有的锁会和主事务合并;一旦触发逻辑涉及写操作(比如更新计数器表),就等于在原事务里“悄悄多加了一次行锁请求”。而开发者往往忽略这点,导致锁顺序失控。
stats_counter)时,不同事务按不同顺序争抢同一行,极易形成「事务A锁住id=1 → 等id=2」、「事务B锁住id=2 → 等id=1」的环路SELECT ... FOR UPDATE 或 UPDATE 时,若目标行无索引,InnoDB 可能升级为间隙锁(GAP LOCK)甚至临键锁(NEXT-KEY LOCK),锁住本不需要的范围这是最常见也最容易复现的场景:你建了一张 group_count 表存各 group 的用户数,再用三个触发器同步维护。当每秒几十个 INSERT INTO customers 并发进来时,死锁日志里常出现:
Deadlock found when trying to get lock; try restarting transaction
根本原因不是触发器写错了,而是所有触发器都试图 UPDATE group_count SET cnt = cnt + 1 WHERE group_id = ? —— 这条语句在 group_id 无索引时会锁全表;即使有索引,多个事务对同一 group_id 的并发更新也会因锁顺序/间隙锁叠加而卡住。
group_count.group_id 是主键或唯一索引,且该字段在触发器中始终以确定顺序参与更新INSERT ... ON DUPLICATE KEY UPDATE 替代 UPDATE,但没配 INSERT ... SELECT 的显式锁提示,仍可能触发间隙锁竞争
别猜,直接看死锁日志(SHOW ENGINE INNODB STATUS\G 输出中的 LATEST DETECTED DEADLOCK 段)。重点抓三处:
UPDATE group_count SET ... WHERE group_id = 123,基本锁定是触发器引发的资源争抢HELD LOCKS)和等待哪些锁(WAITING FOR THIS LOCK TO BE GRANTED),确认是否跨表(如主表 + 汇总表)形成锁依赖环真正棘手的从来不是“会不会死锁”,而是“为什么每次都是这条触发器报错,但单测又从不复现”——因为死锁只在特定并发时机、特定数据分布、特定锁等待序列下才爆发。上线前不做批量压测 + 死锁日志采集,等于裸奔。