触发器无法记录慢查询来源,因其不感知执行时间、无法获取客户端ip/sql文本等上下文,也不能测量耗时;只有slow_query_log+log_output=table能天然满足“来源+耗时+sql内容”三位一体监控需求。

不能用触发器记录慢查询来源。 触发器本身不感知“执行时间”,也无法捕获外部 SQL 的耗时;它只在 INSERT/UPDATE/DELETE 事件发生时按序执行逻辑,和慢查询日志(slow_query_log)是两套完全无关的机制。
为什么触发器无法替代慢查询日志
触发器运行在语句执行的中间阶段(BEFORE 或 AFTER),但 MySQL 并不向触发器暴露当前语句的执行耗时、客户端 IP、线程 ID、原始 SQL 文本等关键上下文。你无法在触发器里判断“这条 UPDATE 花了 8 秒”,更无法知道它是不是被慢查询日志判定为慢 SQL。
常见误解是想在 AFTER UPDATE 里写入一张日志表并打上时间戳,但这只记录“操作发生了”,不是“操作很慢”。哪怕一条语句执行了 30 秒,只要没被 long_query_time 捕获,触发器就无从得知。
- 触发器不拦截或观测语句执行过程,只响应已完成/将发生的 DML 事件
-
SLEEP()、大范围UPDATE、未走索引的WHERE等真正慢的操作,触发器内部无法测量其耗时 - 触发器无法获取调用方信息(如应用名、连接用户 host、SQL 原始文本),这些恰恰是定位慢查询来源的关键字段
真正能记录“慢查询来源”的只有 slow_query_log + log_output=TABLE
MySQL 原生支持把慢查询日志写进数据表 mysql.slow_log,该表自带 user_host、query_time、sql_text、rows_examined 等字段,天然满足“来源+耗时+SQL 内容”三位一体监控需求。
启用方式很简单:
- 确保已开启:
SET GLOBAL slow_query_log = ON - 设阈值(例如 2 秒):
SET GLOBAL long_query_time = 2.0 - 强制写入表而非文件:
SET GLOBAL log_output = 'TABLE' - 验证是否生效:
SELECT * FROM mysql.slow_log ORDER BY start_time DESC LIMIT 5
注意:log_output 改为 'TABLE' 后,slow_query_log_file 将被忽略;且 mysql.slow_log 是 CSV 引擎表,不支持索引,查历史需配合 start_time 范围过滤。
如果非要关联“特定表的操作”做耗时分析,得换思路
触发器做不到自动识别“慢”,但可以配合手动埋点 + 外部监控实现近似效果:
- 在目标表的
BEFORE UPDATE触发器中写入INSERT INTO audit_log (table_name, op_type, start_time) VALUES ('orders', 'UPDATE', NOW(6)) - 在
AFTER UPDATE中再写一条结束记录,用NOW(6)计算差值(注意:触发器内无法跨行原子更新同一行,需用临时表或应用层配对) - 更稳妥的做法是:在应用代码里用
SELECT CONNECTION_ID()获取线程 ID,结合performance_schema.events_statements_history_long查该线程最近执行的语句及timer_wait - 或者直接用
pt-query-digest解析文件型慢日志,按SELECT ... FROM orders WHERE ...正则匹配来源表
这些都不是触发器“自动”完成的,都需要额外设计与权衡——比如 performance_schema 默认关闭部分消费者,开销比慢日志更高;而正则匹配依赖 SQL 格式稳定,容易漏判。
真正复杂的地方在于:你想监控的“慢”,本质是服务端执行耗时,而触发器只是 DML 的钩子,它既不计时、也不知情、更不归档原始语句。别试图让触发器干慢日志的活,先确认你要的是“谁改了这张表”,还是“哪条 SQL 执行太久”——二者路径完全不同。











