需先执行set optimizer_trace="enabled=on,one_line=off",再执行目标select语句,最后查询information_schema.optimizer_trace获取解析与优化各阶段耗时。

如何开启 optimizer_trace 并捕获 SQL 解析阶段耗时
MySQL 的 optimizer_trace 不是实时监控工具,它只在语句执行后、优化器完成逻辑计划生成时,把内部决策过程快照写入会话变量 information_schema.optimizer_trace。它不记录执行阶段(如扫描行数、IO 等),但能清晰反映 SQL 解析、条件化简、索引选择、连接顺序等环节的耗时和分支判断。
开启方式很简单,但必须在同个会话中按顺序执行:
- 设置
SET optimizer_trace="enabled=on,one_line=off"(one_line=off更易读) - 执行目标 SQL(必须是 SELECT;INSERT/UPDATE/DELETE 仅部分版本支持,且 trace 内容不完整)
- 查询
SELECT * FROM information_schema.optimizer_trace
注意:optimizer_trace 默认关闭,且每个会话独立;trace 结果只保留最近一次 SELECT 的,多次执行会覆盖。
从 trace 输出里定位“解析”与“优化”各阶段耗时
真正被称作“SQL 解析过程”的耗时,在 trace 中其实分散在几个关键字段里:steps 数组里的 join_preparation(语法树构建与语义检查)、condition_processing(WHERE/HAVING 条件标准化)、range_analysis(索引范围估算)和 considered_execution_plans(备选执行计划评估)。它们各自带有一个 duration 字段(单位秒,精度微秒级)。
例如,你看到如下片段:
{"select#": 1, "steps": [{"join_preparation": {"select#": 1, "duration": "0.000042"}}, {"condition_processing": {"condition": "WHERE", "original_condition": "a > 1 AND b = 2", "steps": [{"transformation": "equality_propagation", "resulting_condition": "a > 1 AND b = 2", "duration": "0.000015"}]}}]}
这里 join_preparation.duration 就是语法解析+语义绑定阶段的真实耗时;而 condition_processing.duration 是条件重写所花时间——不是网络或磁盘延迟,纯 CPU 计算开销。
常见干扰项:steps 外层的 duration 是整个优化器总耗时,不能代表“SQL 解析”,它包含所有子步骤叠加,且不含 query cache 查找或权限校验等前置动作。
为什么有些 SQL 在 trace 里看不到 duration 或显示为 0
这不是 bug,而是 MySQL 的 trace 实现机制决定的:
- 简单常量查询(如
SELECT 1)跳过优化器主流程,直接走“const table”路径,optimizer_trace可能为空或只有极简结构 - 使用了缓存结果(如 query cache 启用且命中),优化器根本不会运行,自然无 trace
- 语句被拒绝(权限不足、语法错误未到优化阶段),trace 不触发;错误会先报在
SHOW WARNINGS,而非 trace 表 - MySQL 5.7.8 之前版本对
duration支持不全,部分步骤无该字段;建议至少用 5.7.10+ 或 8.0.20+
验证是否真触发了优化:执行后立刻查 SELECT ROWS_EXAMINED FROM performance_schema.events_statements_current WHERE SQL_TEXT LIKE '%your_sql%',非零说明优化器已介入。
结合 performance_schema 定位真实端到端瓶颈
optimizer_trace 只告诉你“优化花了多久”,但用户感知的“SQL 慢”往往卡在别处:网络传输、锁等待、磁盘 IO、大结果集序列化。这时候单看 trace 会误判。
更实用的做法是组合诊断:
- 用
SET profiling = 1+SHOW PROFILES看整体阶段耗时分布(parse、optimize、execute、end 等) - 查
performance_schema.events_statements_history_long,过滤SQL_TEXT和TIMER_WAIT,确认是否 optimize 阶段真占大头 - 若
optimizer_trace.duration是毫秒级,但语句总耗时几百毫秒,大概率问题出在executing或sending data阶段,该去查innodb_row_lock_time或Handler_read_*状态变量
真正容易被忽略的是:optimizer_trace 本身有性能开销(尤其复杂 JOIN),线上长期开启会导致吞吐下降 5–10%,切勿全局启用,只在排查具体慢 SQL 时临时打开。











