触发器必然拖慢写入,这是事务内同步执行的硬开销;优化方向只有砍掉非必要逻辑或把耗时操作移出事务,真实瓶颈常藏于触发器内部且默认不被慢日志记录。

触发器必然拖慢写入,这不是配置能调出来的“慢”,而是事务内同步执行的硬开销。优化方向只有两个:砍掉非必要逻辑,或把耗时操作移出事务。
确认是不是触发器在拖慢插入
别只看慢查询日志里那条 INSERT 耗时高——它可能只是表象。真实瓶颈往往藏在触发器内部,而 MySQL 默认不记录这部分耗时。
- 先启用 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→ 和主表行锁竞争,MySQL 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,只建必要索引
批量插入时触发器性能断崖式下跌
INSERT INTO t1 SELECT * FROM t2 LIMIT 10000 这类操作会让触发器执行 10000 次,哪怕每次只花 0.3ms,总延迟也超 3 秒,且锁持有时间拉长,极易触发 Lock wait timeout exceeded。
- 批量导入前,用
ALTER TABLE t1 DISABLE TRIGGER trigger_name(MySQL 8.0.19+ 支持)临时禁用 - 禁用后执行完再
ENABLE TRIGGER,避免在事务中反复启停 - 不要依赖
INSERT DELAYED—— 它已废弃,不推荐
最难的不是写触发器,是判断哪一行逻辑不该放在数据库里执行。越靠近业务侧的操作,越容易因外部依赖、重试机制、监控埋点等需求,在触发器里变成不可控的黑盒。


















