触发器性能瓶颈不会出现在慢查询日志里——slow_query_log默认不记录触发器内部语句,只记录主sql;其耗时直接叠加到主dml响应时间,需通过performance_schema或打点日志定位。

触发器性能瓶颈不会出现在慢查询日志里——slow_query_log 默认不记录触发器内部语句,只记主SQL。你看到的“慢”,大概率是触发器在背后拖慢了整个事务。
为什么慢查询日志查不到触发器里的慢SQL
MySQL 的 slow_query_log 只捕获显式执行的 SQL 语句,而触发器内嵌的 SELECT、UPDATE 或函数调用属于“隐式执行”,不在默认记录范围内。即使触发器里有一条查百万行的 SELECT * FROM orders WHERE status = 'pending',只要主语句本身快,它就不会进慢日志。
- 现象:接口 P95 耗时突增,但
mysqldumpslow -s t -t 10没发现可疑 SQL - 验证方式:在触发器开头加一句
INSERT INTO debug_log VALUES (NOW(), CONNECTION_ID(), 'start');,再对比SHOW PROCESSLIST中长时间处于Updating或Sending data状态的线程 - 根本原因:触发器运行在主事务上下文中,它的耗时直接叠加到主 DML 的响应时间上,但不单独暴露
用 performance_schema 抓触发器真实执行耗时
这是目前最可靠的定位手段,能精确到触发器内某条语句的执行时间。
- 先启用相关采集器:
UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME LIKE 'events_statements_%'; - 查历史记录:
SELECT SQL_TEXT, TIMER_WAIT, ROWS_AFFECTED FROM performance_schema.events_statements_history_long WHERE SQL_TEXT LIKE '%TRIGGER%' OR SQL_TEXT LIKE '%your_table_name%'; - 注意
TIMER_WAIT单位是皮秒(ps),除以 10^12 得到秒数;重点关注ROWS_AFFECTED异常高的语句 - 如果返回为空,检查
performance_schema是否启用:SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME = 'performance_schema';,必须为ON
触发器里哪些写法一查就慢
不是所有 SELECT 都等价。触发器中低效查询会立刻放大锁冲突和执行延迟。
-
SELECT ... FROM big_table WHERE status = 'pending' AND created_at > NOW() - INTERVAL 1 HOUR:没复合索引时,type=ALL+rows=100万+是常态 -
SELECT * FROM users JOIN orders ON users.id = orders.user_id WHERE orders.id = NEW.order_id:关联查多表 → 锁范围扩大,易触发Lock wait timeout exceeded -
SELECT COUNT(*) FROM logs WHERE order_id = NEW.id:在BEFORE INSERT中用NEW.id查表 →NEW.id还未生成,结果为空或报错,但查询仍执行并加间隙锁 -
SELECT ... FROM same_table WHERE id != NEW.id:查同表且非主键条件 → 极易形成自锁或死锁,尤其高并发 UPDATE 场景
EXPLAIN 看不出触发器慢,但能暴露底层隐患
对触发器内 SQL 单独 EXPLAIN 有意义,但它不反映事务隔离级别带来的真实锁行为。
-
EXPLAIN显示key=PRIMARY,不代表不慢:如果主表正在被高频写入,InnoDB 聚簇索引页分裂会导致每次WHERE id = NEW.id定位都要读多个磁盘页 -
Extra=Using filesort在触发器里很危险:意味着排序没走索引,可能触发磁盘临时表,而触发器事务无法中断,只能硬扛 - 区分
NEW.status != OLD.status和OLD.status IS DISTINCT FROM NEW.status:前者遇 NULL 直接失效,导致本该跳过的逻辑全执行,白白增加开销 - 别信
rows字段:触发器中查的往往是小结果集,但rows显示大数,说明统计信息过期,执行计划已失真,需ANALYZE TABLE
真正卡住的往往不是某一行代码,而是触发器把“最终一致性操作”塞进了强一致性事务——比如一边扣库存一边发 MQ,只要 MQ 服务抖动,整个订单插入就卡住。定位时得跳出 SQL 层,先看 Innodb_row_lock_waits 是否飙升,再回溯到触发器逻辑是否承担了不该承担的职责。











