触发器必然拖慢insert,修复只能砍非必要逻辑或移出事务;需用performance_schema定位真实开销,避免在触发器中跨表查询、索引失效操作、锁竞争及非确定性函数。

直接结论:触发器必然拖慢 INSERT,这不是配置能优化的“慢”,而是事务内同步执行的硬性开销。修复方向只有两个——砍掉非必要逻辑,或把耗时操作移出事务。
确认是不是触发器在拖慢插入
别只看慢查询日志里那条 INSERT 耗时高,得定位到触发器本身。MySQL 没有原生 Profiler,但可以用 performance_schema 抓真实开销:
- 先确保开启:
UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME IN ('events_statements_history', 'events_stages_history') - 执行一次插入后查:
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才是秒数;如果看到statement/sql/select或statement/sql/insert占比高,基本就是触发器里的 SQL 在吃时间
触发器里最常踩的性能坑
90% 的慢不是语法错,而是它在事务里干了不该干的事:
-
BEFORE INSERT里写SELECT balance FROM accounts WHERE user_id = NEW.user_id,而accounts.user_id没索引 → 全表扫描 + 行锁,其他插入全被堵住 -
WHERE UPPER(email) = UPPER(NEW.email)→ 索引失效,每次插入都扫全表 -
AFTER INSERT里UPDATE order_summary SET total = total + NEW.amount→ 和主表行锁竞争,8.0+ 更容易报Deadlock found when trying to get lock - 触发器里调用存储过程或自定义函数 → 每次调用都带上下文切换和隐式子事务,批量插入时开销指数放大
真正有效的修复路径
不是“优化触发器”,而是重新划清同步与异步边界:
- 必须保同步的,只留最轻量逻辑:
SET NEW.created_at = NOW()、IF NEW.amount - 所有跨表查询、写日志、更新统计、发通知、调外部 API,一律移出触发器 —— 改由应用层异步发 MQ,或监听 binlog(用
canal/maxwell)消费 - 真要保留中转动作,用极简
INSERT INTO trigger_queue (id, table_name) VALUES (NEW.id, 'orders'),这张表引擎选MEMORY或精简InnoDB,只建必要索引 - 批量导入前,MySQL 8.0.19+ 可临时禁用:
ALTER TABLE orders DISABLE TRIGGER ins_log_trigger,完事再ENABLE
容易被忽略的细节
很多人以为“删掉触发器里的 SELECT 就行了”,但实际卡点常藏在更底层:
-
NOW()、UUID()这类非确定性函数,在高并发下会争抢内部时钟/随机数生成器锁,单次调用可能多等 0.5–2ms - 触发器里写了
INSERT INTO log_table SELECT *,哪怕目标表很小,也会触发 gap lock,阻塞后续插入 - 多个触发器按字母顺序执行,嵌套调用时顺序不可控,调试成本远高于重写逻辑
- 线上误留
SELECT 'debug'或往 debug 表写日志,这种语句在 QPS > 50 时就会成为瓶颈











