触发器在大并发写入下必然拖垮性能,因其强制每行同步执行逻辑,导致批量update退化为n次串行小事务,锁、日志、cpu均被单行事务绑定,且触发器内未优化sql会引发全表扫描、锁升级与死锁,而其开销不暴露于常规监控。

触发器在大并发写入下必然拖垮性能,不是“可能慢”,而是设计机制决定了它无法批量处理——每行写入都强制同步执行一遍逻辑,锁、日志、CPU全被绑死在单行事务里。
触发器让批量UPDATE变成N次串行小事务
MySQL对 UPDATE t SET x = 1 WHERE id IN (1,2,3,...,10000) 不会整体触发一次,而是逐行调用触发器。哪怕触发器只有一行 NEW.updated_at = NOW(),也会因系统时钟函数争用累积延迟;若含 SELECT 或自定义函数,单行开销可能从 0.3ms 拉到 8ms+,10 万行就是 800 秒。
-
SHOW PROCESSLIST中大量线程卡在Updating状态 - 慢查询日志里同一条
UPDATE反复出现,每次耗时稳定在几毫秒以上 -
INFORMATION_SCHEMA.PROFILING显示触发器逻辑占总耗时 70%+
触发器内SQL没走索引=每改一行就扫一次全表
比如 BEFORE UPDATE 里写 SELECT COUNT(*) FROM logs WHERE user_id = OLD.user_id,看着只查一行,但若 logs(user_id) 没索引,或类型不匹配(如 INT vs BIGINT),EXPLAIN 就会返回 type: ALL ——这不是“慢一点”,是每改一行就触发一次全表扫描。
- 必须单独把触发器里的 SQL 拎出来
EXPLAIN,不能只看主语句 -
SHOW INDEX FROM logs看不到隐式转换导致的索引失效,得人工核对字段类型 -
WHERE条件里混用函数(如DATE(created_at))会让索引彻底失效
触发器延长锁持有时间且扩大锁范围
主 SQL 可能只锁 orders.id = 123 这一行,但触发器里执行 UPDATE stats SET cnt = cnt + 1 WHERE type = NEW.type,如果 stats(type) 没索引,就会升级成表级锁;更糟的是,这个锁要等到整个事务提交才释放——而事务结束时间由触发器决定。
-
SHOW ENGINE INNODB STATUS里看到大量LOCK WAIT,trx_query显示的是主 SQL,但阻塞源在触发器 -
innodb_row_lock_time_avg突增,但slow_query_log里找不到对应慢 SQL——因为触发器耗时不单独记录 - 两个并发事务分别更新
orders和users,若彼此触发器又去更新对方表,Deadlock found when trying to get lock几乎必然发生
真正难绕开的点是:触发器逻辑不进慢日志、不进 EXPLAIN、不暴露在监控指标里,你看到的只是“这条 UPDATE 变慢了”,却很难一眼定位到慢在触发器里那条没索引的 SELECT。排查时得从 performance_schema.events_statements_history_long 里捞出含 TRIGGER 的事件再分析,否则永远在猜。











