触发器必然导致批量写入性能断崖式下降,因其强制逐行同步执行、无法并行,10万行即10万次开销,且触发器内SQL不进慢日志、不进EXPLAIN,极易因索引失效引发全表扫描和锁升级。

因为触发器强制在主事务内逐行同步执行,每插入或更新一行就调用一次,10 万行就是 10 万次开销——这不是“可能慢”,而是 MySQL 内核机制决定的硬约束。
触发器让批量写入彻底退化成串行小事务
MySQL 对 INSERT INTO t VALUES (),(),... 或 UPDATE t SET x=1 WHERE id IN (1,2,3,...) 这类语句,不会整体触发一次,而是对每一行单独执行触发器逻辑。哪怕触发器只有一行 SET NEW.updated_at = NOW(),高并发下系统时钟函数争用也会累积延迟;若含 SELECT 或自定义函数,单行耗时从 0.3ms 拉到 8ms+ 很常见。
-
SHOW PROCESSLIST中大量线程卡在Updating或Executing trigger状态 - 慢查询日志里同一条
UPDATE反复出现,每次耗时稳定在几毫秒以上 -
INFORMATION_SCHEMA.PROFILING(已弃用)或performance_schema.events_stages_history显示触发器逻辑占总耗时 70%+
触发器内 SELECT 是最隐蔽的性能杀手
它不进慢日志、不进 EXPLAIN、不暴露在常规监控里,但每次执行都拉长锁持有时间、扩大锁范围,且极易因索引失效引发全表扫描。
-
BEFORE INSERT里写SELECT balance FROM accounts WHERE user_id = NEW.user_id,而accounts.user_id没索引 → 全表扫描 + 行锁阻塞其他写入 -
WHERE UPPER(email) = UPPER(NEW.email)→ 索引彻底失效,每次插入都扫全表 -
AFTER INSERT中JOIN三张大表做校验 → 单次插入变成嵌套循环,扫描行数指数级上升 - 所有这些
SELECT都被纳入当前事务,无法绕过,也不能异步
空触发器也有不可忽略的固定开销
哪怕触发器 body 是空的,或只写 SET @x = 1,MySQL 仍需完成解析、权限检查、上下文切换等动作,实测带来 0.1–0.3ms 固定延迟。更关键的是:SET SQL_LOG_BIN = 0 可跳过 binlog,但无法跳过触发器执行——它就是事务的一部分。
- 调用
NOW()、UUID()、RAND()等非确定性函数,在高并发下争抢系统资源,单次增加 0.3–0.8ms - 多个触发器争抢同一张统计表(如
user_summary)主键 → 高概率触发Deadlock found when trying to get lock - 触发器数量超几十个时,
metadata lock等待明显上升,ALTER TABLE都可能被卡住
真正难绕开的点是:你看到的只是“这条 INSERT 变慢了”,却很难一眼定位到慢在触发器里那条没索引的 SELECT——它不记录在慢日志,不显示在 EXPLAIN,也不出现在大多数监控指标里。


















