需先执行SET optimizer_trace="enabled=on", end_markers_in_json=on;再执行目标SQL,最后查询information_schema.optimizer_trace表的TRACE字段获取JSON格式的优化器决策快照。

如何开启并获取 optimizer_trace 输出
MySQL 的优化器内部决策过程默认不对外暴露,必须手动开启追踪开关,并执行目标 SQL 后主动查询系统表才能拿到结果。optimizer_trace 不是日志文件,也不是实时流,而是一次性快照。
实操建议:
- 先执行
SET optimizer_trace="enabled=on", end_markers_in_json=on;(end_markers_in_json让嵌套结构更易读) - 再执行你想分析的 SQL(比如
SELECT * FROM orders WHERE user_id = 123;) - 最后查
SELECT * FROM information_schema.optimizer_trace;,结果在TRACE字段里,是 JSON 格式 - 注意:每个会话独立生效,且只记录**最近一条**可追踪语句;重复执行会覆盖前一次结果
optimizer_trace 中关键字段怎么看
JSON 输出里真正有用的不是顶层字段,而是 steps 数组里的每一步推导——尤其是 considered_execution_plans、condition_processing 和 ref_optimizer_key_uses 这几块。
常见错误现象:看到 "rows_estimation": [...] 里预估行数和实际差十倍,却找不到原因。其实得往 condition_processing 里看 MySQL 是否把 WHERE 条件重写了,或者是否因隐式类型转换丢掉了索引。
使用场景举例:
- 当
EXPLAIN显示用了索引但查询仍慢,就去TRACE里查"using_index": false是在哪步被判定的 - 发现
"range_analysis"阶段跳过了某个本该可用的索引,大概率是统计信息过期或条件写法触发了索引失效(如LIKE '%abc') -
"analyzing_range_alternatives"下如果出现"index": "PRIMARY"但"rows": 999999,说明优化器认为全表扫比走主键范围更快——这时候要怀疑是不是缺少复合索引
为什么开了 optimizer_trace 却看不到结果
最常见原因是没关掉自动提交(autocommit=1),导致 SELECT 执行完立刻提交,而 optimizer_trace 在事务提交后清空内容;另一个是权限问题:information_schema.optimizer_trace 需要 PROCESS 权限。
实操建议:
- 执行前加
SET autocommit = 0;,查完optimizer_trace再COMMIT;或ROLLBACK; - 确认用户有
PROCESS权限:SHOW GRANTS;查有没有GRANT PROCESS ON *.* -
optimizer_trace_max_mem_size默认只有 1MB,复杂查询可能被截断,可临时调大:SET optimizer_trace_max_mem_size=4*1024*1024; - 某些版本(如 MySQL 5.7.20+)对子查询、CTE 支持不完整,
TRACE可能为空或只含顶层计划
和 EXPLAIN FORMAT=TREE 搭配用更有效
EXPLAIN FORMAT=TREE 是 MySQL 8.0+ 提供的可视化执行计划,它展示的是优化器最终选定的路径;而 optimizer_trace 展示的是所有被考虑过、又被拒绝的路径。两者互补,不能互相替代。
性能影响很小,但别长期开着:optimizer_trace 本身不拖慢查询,但生成 JSON 和内存拷贝有微小开销;更重要的是,它掩盖不了真实瓶颈——比如 TRACE 显示走了索引,但磁盘 I/O 高,那问题其实在 buffer pool 大小或数据局部性上。
容易被忽略的一点:optimizer_trace 不包含执行阶段的信息(如实际扫描行数、临时表使用、排序是否用到磁盘),这些得靠 performance_schema 或慢日志里的 Rows_examined 字段补全。










