触发器不显式出现在主insert执行计划中,需通过开启general_log查看实际执行的sql,或单独explain触发器内每条语句;其insert select易引发不可见的gap lock,导致锁表和性能抖动。

怎么看触发器到底执行了哪些SQL
触发器不显式出现在主INSERT的执行计划里,但它的每条语句都会真实执行、加锁、刷日志。最直接的办法是开general_log,复现一次插入操作,然后翻日志看MySQL实际干了什么:
- 在测试库执行
SET GLOBAL general_log = ON;,日志默认写入/var/lib/mysql/hostname.log - 跑一条带数据的
INSERT INTO t1 VALUES (...); - 关掉日志
SET GLOBAL general_log = OFF;,用tail -n 50查最后几十行
你常会看到类似这样的记录:INSERT INTO audit_log SELECT ... FROM t1 WHERE id = ? 或 UPDATE counter_table SET cnt = cnt + 1 WHERE key = ?——这些就是触发器悄悄干的事。别只盯着主语句,它只是个“引子”。
EXPLAIN没法分析触发器,但能分析它的每一行SQL
EXPLAIN对主INSERT语句有效,但对触发器里的SELECT、UPDATE完全不显示。真正有用的做法是:把触发器定义里每条独立SQL拎出来,单独EXPLAIN。
- 先用
SHOW CREATE TRIGGER trigger_name拿到完整定义 - 找出所有
SELECT、UPDATE、INSERT INTO ... SELECT语句 - 对每条都执行
EXPLAIN FORMAT=TRADITIONAL,重点看key是否为NULL、rows是否过大、type是不是ALL或index
常见陷阱:主语句EXPLAIN显示走了索引,但触发器里一句SELECT * FROM big_table WHERE status = 1没加索引,导致每次INSERT都在等全表扫描。
触发器里用INSERT SELECT最容易锁表
这是线上高频踩坑点。INSERT INTO log_table SELECT ... FROM main_table WHERE ...这类语句在触发器中执行,会持有一个gap lock,不仅自己慢,还会堵住其他事务的INSERT。
- 如果
main_table有大范围WHERE条件(比如created_at > '2024-01-01'),InnoDB会锁住整个索引区间 - 更糟的是,这种锁是“不可见”的——你查
INFORMATION_SCHEMA.INNODB_TRX可能只看到主INSERT在等,根本想不到是触发器里的SELECT在锁 - 临时规避办法:在触发器开头加
SET @disable_trigger = 1;,再在业务层用变量控制是否跳过;长期方案是把这类逻辑移到应用层异步处理
真正难排查的不是“慢”,而是“慢得没规律”:有时候快,有时候卡几秒,往往就是触发器里某条SQL在不同参数下走出了不同执行路径,索引失效或锁范围突变。











