mysql触发器变慢主因是事务内同步执行低效查询或写入;需用slow_query_log+explain及performance_schema分析,禁用无索引select、日志表须innodb+索引,上线前清理重复触发器。

触发器变慢,90%不是触发器本身的问题,而是它在事务里同步执行了不该做的查询或写入。
MySQL 没有 SQL Profiler,别被名字带偏
SQL Server 才有原生 SQL Server Profiler;MySQL 没这玩意儿。想看触发器耗时,得换路子:用 slow_query_log + EXPLAIN 组合,再配合 information_schema.PROFILING(已弃用)或更靠谱的 performance_schema 表。误以为开个 Profiler 就能“点开看耗时”,结果发现 MySQL 根本不支持——这是最常见的起点错误。
实操建议:
- 先确认 MySQL 版本是否 ≥ 5.6,然后启用
performance_schema(默认开启,但部分表可能 disabled) - 执行触发器相关 DML 前,运行:
UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME IN ('events_statements_history', 'events_stages_history'); - 触发 INSERT/UPDATE 后,查:
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 得秒数,别直接当毫秒读
触发器里的 SELECT 是性能杀手,尤其在 BEFORE 触发器中
MySQL 触发器内允许 SELECT,但不允许修改当前正在操作的表(Can't update table 't' in stored function/trigger)。可很多人没意识到:哪怕只是查另一张关联表,只要没索引、或用了函数包装字段,就会拖慢整个 DML。
常见错误现象:
- BEFORE INSERT 触发器里写
SELECT balance FROM accounts WHERE user_id = NEW.user_id;,而accounts.user_id没索引 → 全表扫描,锁住整张表 - WHERE 条件写成
WHERE UPPER(email) = UPPER(NEW.email)→ 索引失效,每次插入都扫全表 - 触发器里 JOIN 三张表做校验 → 单次插入变成多表嵌套循环,rows 扫描量指数级上升
实操建议:
- 把这类
SELECT拆到应用层,在 INSERT 前查好balance或email_exists,作为参数传入 - 如果必须留在 DB 层,确保被查字段有覆盖索引,且条件能走索引(比如用
=而非LIKE '%@gmail.com') - 避免在 BEFORE 触发器里查刚要插入的同一张表(MySQL 显式禁止),也尽量别在 AFTER 里查自己——容易隐式递归或抖动
日志类触发器最容易堆出性能雪崩
为每条订单插入都写一条 INSERT INTO audit_log,看着逻辑清晰,实际是吞吐量断崖的元凶。audit_log 表如果没有主键、没有索引、引擎还是 MyISAM,那高并发下就是锁队列+磁盘 IO 瓶颈。
使用场景:
- 审计、变更追踪、调试用途
- 但生产环境日均百万级写入时,同步写日志表会直接卡住主业务表的 INSERT
实操建议:
- audit_log 表必须用
ENGINE=InnoDB+ 主键(如自增 ID 或时间戳+UUID)+ 至少一个二级索引(比如(table_name, event_time)) - 更激进方案:改用
ENGINE=BLACKHOLE(只丢弃不落盘),配合 binlog 解析做异步归档 - 或者干脆放弃触发器,让应用在事务提交后发消息到 Kafka,由下游消费写日志 —— 这才是现代架构的真实解法
重复触发器和幽灵触发器常被忽略
开发过程中反复 CREATE TRIGGER 却没 DROP,MySQL 允许多个同名触发器共存(只要 EVENT+TIME+TABLE 组合不同)。上线后你看到的是一份逻辑,实际跑的是三份叠加逻辑 —— 耗时直接 ×3,还查不出原因。
实操建议:
- 上线前必跑:
SHOW TRIGGERS LIKE 'orders';,确认数量是否为预期值(通常应是 1 个 BEFORE INSERT、1 个 AFTER UPDATE 等) - 查全量:
SELECT TRIGGER_NAME, EVENT_MANIPULATION, EVENT_OBJECT_TABLE, ACTION_TIMING FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA = 'your_db'; - 部署脚本必须写成:
DROP TRIGGER IF EXISTS tr_orders_before_insert; CREATE TRIGGER tr_orders_before_insert BEFORE INSERT ...
真正难调的从来不是单个触发器慢,而是多个触发器叠加、嵌套调用存储过程、又混着没索引的 SELECT —— 这种组合问题,靠 Profiler 看不出根因,得一层层剥开 execution plan 和 performance_schema 的嵌套事件链。










