慢查询日志不记录触发器内部SQL,仅显示主DML语句,掩盖真实耗时;应启用performance_schema捕获触发器语句,或建诊断表打点计时,最直接方法是禁用触发器对比耗时差异。

触发器慢写入为什么不能只看慢查询日志
因为 slow_query_log 默认不记录触发器内部执行的 SQL,只记录主 DML 语句(如 INSERT INTO orders),哪怕触发器里跑了 5 秒的 SELECT COUNT(*) FROM huge_log_table,慢日志里也完全看不到。你看到的“快”,只是表象。
用 performance_schema 抓取触发器真实耗时
必须开启相关消费者才能捕获触发器语句:
UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME LIKE 'events_statements_%';UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME LIKE 'statement/sql/%';
之后查 performance_schema.events_statements_history_long,过滤条件要带触发器特征:
WHERE SQL_TEXT LIKE '%TRIGGER%' OR EVENT_NAME LIKE '%trigger%'- 或更实用:按目标表名查,
WHERE SQL_TEXT LIKE '%INSERT INTO target_table%',再人工确认是否含触发器逻辑
注意:TIMER_WAIT 字段单位是皮秒,除以 1000000000000 才是秒级耗时。
在触发器里加简易诊断日志到自建表
适用于快速验证某几个关键触发器是否拖慢写入,不依赖全局配置:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 先建一张轻量诊断表:
CREATE TABLE trigger_debug_log (id BIGINT PRIMARY KEY AUTO_INCREMENT, trigger_name VARCHAR(64), start_time DATETIME(6), end_time DATETIME(6), duration_ms DECIMAL(10,3), conn_id INT); - 在触发器开头插入起始时间:
INSERT INTO trigger_debug_log (trigger_name, start_time, conn_id) VALUES ('tr_orders_after_insert', NOW(6), CONNECTION_ID()); - 结尾补上结束时间和计算耗时(需用变量):
SET @start := (SELECT start_time FROM trigger_debug_log WHERE id = LAST_INSERT_ID()); INSERT INTO trigger_debug_log (trigger_name, start_time, end_time, duration_ms, conn_id) VALUES ('tr_orders_after_insert', NOW(6), NOW(6), TIMESTAMPDIFF(MICROSECOND, @start, NOW(6))/1000, CONNECTION_ID());
缺点:每次调用都多两次 INSERT,仅限临时排查;线上长期启用会反向加重写入压力。
对比禁用触发器前后的 DML 耗时差异
这是最直接、最不可辩驳的判断依据,尤其适合批量操作场景:
- MySQL 8.0.19+ 支持:
ALTER TABLE orders DISABLE TRIGGER tr_orders_after_insert; - 执行同一批
INSERT或LOAD DATA,用BENCHMARK()或客户端计时对比 - 恢复:
ALTER TABLE orders ENABLE TRIGGER tr_orders_after_insert;
如果禁用后耗时从 8s 降到 0.3s,那问题根源基本就锁定了——别再猜索引或 buffer pool,先砍触发器逻辑。
真正容易被忽略的是:触发器内一次 SELECT ... FOR UPDATE 可能导致整条 INSERT 卡在锁等待队列里,而 SHOW PROCESSLIST 显示状态是 Updating 或 Locked,不是 Trigger —— 它根本不会标出“正在跑触发器”。

















