SQL MySQL 的 optimizer_trace 的执行计划调试与 cost-based 优化器分析

1次阅读

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

SQL MySQL 的 optimizer_trace 的执行计划调试与 cost-based 优化器分析

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_idINT),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_estimationtable_scancostrange_scan 还低,大概率是统计不准或索引选择性太差——这时 trace 不是 bug,是它在老实告诉你:“我算来算去,扫表确实更便宜”。

text=ZqhQzanResources