触发器多会严重拖慢性能,因其在单事务内同步逐行执行——100行插入即触发100×触发器数次逻辑,空触发器也带来0.1–0.3ms固定开销,5个触发器×1000行=至少500ms纯调度延迟,且内置SELECT易因无索引退化为全表扫描;应禁用而非优化,MySQL 8.0.19+支持ALTER TABLE ... DISABLE TRIGGER直接验证根因。

触发器多不等于慢,但所有触发器都在同一INSERT事务里同步、逐行执行——100 行插入 = 100 ×(触发器数量)次逻辑执行,锁、日志、CPU 全被串行化拖死。
每个触发器都强制增加一次事务内开销
哪怕空触发器也会带来 0.1–0.3ms 固定延迟:MySQL 必须做权限检查、上下文切换、NEW/OLD 表初始化。5 个触发器 × 1000 行 = 至少额外 500ms 纯调度开销,还不算里面任何 SQL。
- BEFORE INSERT 触发器中
NOW()或UUID()调用,在高并发下会争用系统时钟或随机数生成器,单次延迟可能跳到 2ms+ - AFTER INSERT 里写日志表,哪怕只插一行,也会触发聚簇索引 + 所有二级索引的 B+ 树维护,IO 压力翻倍
- 多个触发器嵌套(比如 A 触发 B,B 又触发 C)会让执行路径不可控,
INFORMATION_SCHEMA.PROFILING显示总耗时飙升但无法定位哪一层最重
触发器里的 SELECT 很容易退化为全表扫描
你看到的是 INSERT INTO orders 变慢,实际卡在触发器里一句 SELECT COUNT(*) FROM logs WHERE user_id = NEW.user_id——而 logs(user_id) 没索引。
-
EXPLAIN不显示触发器内 SQL,必须手动把触发器里的每条SELECT单独拎出来跑EXPLAIN,重点看type是否为ALL、rows是否远超预期 - 字段类型不一致(如
orders.user_id是BIGINT,logs.user_id是INT)会导致隐式转换,索引直接失效 - WHERE 中用函数(如
DATE(created_at))会让索引无法使用,触发器一执行就扫全表
禁用比优化更有效,且 MySQL 8.0.19+ 支持语法级控制
别花时间给触发器加索引、拆函数、缓存结果——问题不在“怎么写得好”,而在“它本就不该承担批量逻辑”。临时禁用能立刻验证是否是触发器导致的瓶颈。
- 禁用单个触发器:
ALTER TABLE orders DISABLE TRIGGER tr_orders_after_insert - 批量导入前统一禁用:
ALTER TABLE orders DISABLE TRIGGER ALL(MySQL 8.0.19+) - 禁用后对比
performance_schema.events_statements_history_long中同一条INSERT的平均执行时间,若下降 >80%,基本可锁定触发器为根因 - 禁用期间,用
INSERT ... ON DUPLICATE KEY UPDATE或生成列替代自动填充逻辑,避免回退到旧模式
真正难处理的不是“怎么让触发器快一点”,而是它完全不暴露在常规监控里:慢查询日志只记主语句,SHOW PROCESSLIST 只显示 Updating,你得主动去 performance_schema 查带 TRIGGER 的事件才能揪出那条没走索引的 SELECT。



















