触发器必然拖慢INSERT,因其在事务内同步逐行执行,必须等待完成才能提交;AFTER INSERT中单行写日志也会增加2–10ms延迟,高频下TPS常降一个数量级。

触发器必然拖慢INSERT,不是“可能”而是机制决定
MySQL 触发器在事务内同步执行,每行 INSERT 都触发一次,且必须等它执行完才能提交事务。这不是配置能绕开的限制——AFTER INSERT 里哪怕只写一行 INSERT INTO log_table VALUES (NEW.id, NOW()),也会增加 2–10ms 延迟。高频写入下 TPS 掉一个数量级很常见。
常见错误现象:SHOW PROCESSLIST 里大量线程卡在 Updating 或 Sending data;慢查询日志显示某条 INSERT 耗时 80ms,但单独执行不到 5ms——多出来的全是触发器开销。
怎么确认是触发器拖慢了插入
不能只看慢日志里那条 INSERT 耗时高,得证明触发器本身占了多少时间。MySQL 没有 SQL Profiler,别被名字带偏——SQL Server 才有原生 Profiler,MySQL 不支持。
实操建议:
- 启用
performance_schema(MySQL ≥ 5.6 默认开启,但部分表可能 disabled) - 执行前运行:
UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME IN ('events_statements_history', 'events_stages_history') - 触发 INSERT 后查:
SELECT EVENT_NAME, TIMER_WAIT FROM performance_schema.events_stages_history WHERE NESTING_EVENT_ID IN (SELECT EVENT_ID FROM performance_schema.events_statements_history WHERE SQL_TEXT LIKE '%INSERT INTO your_table%') ORDER BY TIMER_WAIT DESC LIMIT 5 -
TIMER_WAIT单位是皮秒,除以10^12才是秒数,别直接当毫秒读
触发器里哪些操作最伤写入性能
触发器变慢,90% 不是触发器语法问题,而是它在事务里同步执行了不该做的操作。
以下行为会显著放大延迟:
-
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 * FROM other_db.users)→ 额外连接开销,无法复用当前事务上下文
所有这些 SELECT 都被纳入当前事务,不仅加锁范围扩大,还可能阻塞其他并发写入。
临时验证与长期优化路径
触发器性能差,别急着删,先关掉看看。MySQL 不支持 DISABLE TRIGGER,但可以用变量绕过。
实操建议:
- 在触发器开头加判断:
IF @disable_triggers IS NULL THEN ... END IF; - 压测或维护时,先执行:
SET @disable_triggers = 1 - 真正要长期解决:把日志、统计、通知类逻辑移出触发器,改用应用层发 MQ 或写入轻量队列表;必须保留在 DB 层的,确保被查字段有覆盖索引,且条件能走索引(比如用
=而非LIKE '%@gmail.com')
最容易被忽略的一点:触发器里任何对 OLD 字段的子查询(如 (SELECT name FROM users WHERE id = OLD.user_id))都会加 S 锁,若主语句已对该行加 X 锁,就可能直接死锁。这类引用应提前在主 SQL 中查好并传入变量,而不是在触发器里现场查。

















