开启 optimizer_trace 后看不到执行计划,是因为它仅记录当前会话中实际执行(而非仅 explain)的 select 语句的优化器决策,且依赖 cost-based 优化器生效;若 trace 字段为 null,通常表示优化器跳过 cost 计算(如无索引可选)。

optimizer_trace 开启后为什么看不到执行计划?
因为默认只记录 EXPLAIN 级别的优化器决策,且仅对**当前会话中实际执行的语句**生效,不是所有 SELECT 都自动 trace。常见错误是开了 trace 却去查没执行过的语句,或者用 EXPLAIN SELECT ... 而非真正执行它。
实操建议:
- 必须先执行
SET optimizer_trace="enabled=on";,再执行目标SELECT(不能只EXPLAIN) - 执行完立刻查
SELECT * FROM information_schema.optimizer_trace;,延迟查可能被后续语句覆盖 - 确保语句走的是 MySQL 8.0+ 的 cost-based 优化器(5.7 默认开启,但某些 hint 或 legacy 模式会绕过)
- 如果返回空结果,检查
trace字段是否为NULL—— 这通常意味着优化器直接跳过了 cost 计算(例如全表扫描且无索引可用)
cost 值在 trace 里怎么看?哪几个字段最关键?
真正影响最终执行计划选择的是 cost 字段下的 total_cost,但它藏得深:路径是 steps → join_optimization → condition_processing → considered_execution_plans → [0] → cost。别被嵌套吓住,重点盯三个值:
-
read_cost:引擎层读取数据的成本(含索引查找、回表、IO 预估) -
eval_cost:Server 层计算 WHERE/HAVING 条件的成本(行数 × 每行判断开销) -
prefix_cost:多表 JOIN 中,到当前表为止的累计成本(比total_cost更反映该表的“贡献”)
注意:这些 cost 是相对值,单位是“随机 IO 次数”,不是毫秒;不同语句间不可直接比大小,但同一语句不同 plan 之间可以比。
为什么 trace 显示用了索引,但实际执行很慢?
因为 optimizer_trace 只反映优化器“认为”的最优路径,不反映真实运行时瓶颈。常见脱节点:
- 统计信息过期:
ANALYZE TABLE没跑过,cardinality错误导致 cost 低估/高估 - 隐式类型转换:比如
WHERE user_id = '123'(user_id是INT),trace 里可能显示走索引,但实际执行时因转换丢索引 - 内存限制:
sort_buffer_size太小,trace 里预估用内存排序,实际触发磁盘临时文件,成本暴增 - 并发干扰:trace 是单次估算,而线上高并发下 buffer pool 竞争、锁等待等完全不在 cost 模型里
这时候要交叉验证:EXPLAIN FORMAT=TREE 看实际访问方式,SHOW PROFILE 看各阶段耗时,别只信 trace 里的数字。
MySQL 5.7 和 8.0 的 optimizer_trace 输出差异大吗?
大。8.0 加了更多 cost 细分项和新 stage,比如 range_analysis 下多了 analyzing_range_alternatives,能看清每个 range scan 的 cost 对比;而 5.7 的 considered_execution_plans 结构更扁平,缺少中间推导过程。
关键区别:
- 8.0 支持
SET optimizer_trace_max_mem_size=1048576;控制 trace 内存上限,5.7 固定 1MB 且不可调 - 8.0 的
missing_bytes_beyond_max_mem_size字段明确告诉你是否截断,5.7 截断后不提示 - 8.0 在
join_execution阶段会输出实际 rows_read,5.7 只有预估,没有运行时反馈
如果你在 8.0 上看到 rows_estimation 里 table_scan 的 cost 比 range_scan 还低,大概率是统计不准或索引选择性太差——这时 trace 不是 bug,是它在老实告诉你:“我算来算去,扫表确实更便宜”。










