触发器在事务内隐式执行,所有操作共享同一锁生命周期,哪怕查一行在rr级别下也会加gap lock,导致并发写入排队等锁;after触发器易因锁链闭环引发死锁,before可赋值避免新增sql;行锁升级为范围锁或表锁主因是索引未命中最左前缀或唯一性要求。

因为触发器在事务内隐式执行,所有操作共享同一锁生命周期——哪怕只是查一行,也会在 REPEATABLE READ 隔离级别下加 gap lock,多个并发写入立刻排队等锁。
触发器里 SELECT 为什么比普通查询更危险
表面是读操作,实际会放大锁范围:
-
SELECT name FROM staff WHERE id = NEW.operator_id在 RR 级别下不只锁匹配行,还锁前后间隙;并发插入日志时,多个事务争抢同一段索引范围锁 - 即使
staff.id有主键,锁仍落在索引页上;高并发写会让该页成为热点,Innodb_row_lock_waits指标飙升 - 若
staff表被其他业务频繁更新,该SELECT还可能触发二级索引回表,延长持锁时间
AFTER 触发器比 BEFORE 更容易死锁
AFTER 在主语句已持锁的前提下再发新 SQL,极易形成锁链闭环:
-
AFTER INSERT ON orders中执行UPDATE order_summary SET total = total + NEW.amount,orders新行锁和order_summary行锁在同一事务内竞争 - 另一事务若按相反顺序(先更新
order_summary再插orders),瞬间 ABBA 循环等待 -
BEFORE可直接用SET NEW.total_amount = ...赋值,不发额外 SQL,零新增锁
哪些写法会让行锁升级成范围锁甚至表锁
不是语法错,而是索引没对上,InnoDB 就会扩大锁粒度:
-
UPDATE product SET status = 'sold' WHERE category_id = 123—— 若category_id没索引或只有非唯一索引,InnoDB 锁整个索引范围 -
WHERE条件必须命中**最左前缀的唯一索引或主键**,否则别指望“只锁一行” - 禁止用
OR、LIKE '%abc'、NOT IN;要用EXISTS替代IN子查询 - 绝对禁用
SELECT ... FOR UPDATE—— 触发器里显式加锁等于主动制造瓶颈
真正难的不是让触发器“跑得更快”,而是让它根本不去碰那些会拉长锁生命周期的操作。只要里面出现一次没走索引的 SELECT 或跨表 UPDATE,就不再是性能问题,而是稳定性红线。











