开 optimizer_trace 不等于能看到有效决策过程——需同会话执行真实sql、调大内存、立即查询,否则trace为空或被截断;重点查steps中join_optimization下的cost与索引分析。

开 optimizer_trace 不等于能看到有效决策过程——它默认只存当前会话最后一条语句的 JSON 快照,且极易因内存限制被截断或清空。必须手动调参、严格同会话执行、逐条验证,否则查到的 TRACE 字段要么为空,要么只有空壳。
为什么 SET optimizer_trace='enabled=on' 后查不到 TRACE 或内容为空
这不是权限或配置错,而是触发条件没对上:
-
EXPLAIN SELECT不会生成 trace,只有真实执行的SELECT/UPDATE/DELETE才触发 - 语句报错(如表不存在、列名错、语法错误)会导致优化器跳过完整优化阶段,
TRACE字段为空或仅含极简结构 -
INSUFFICIENT_PRIVILEGES = 1表示你对某张表/视图没有SELECT权限,trace 强制清空 -
autocommit = 1下,SELECT执行完立刻提交,trace 内容可能被清掉;建议先SET autocommit = 0,查完再ROLLBACK - 必须在同一个会话里:先
SET optimizer_trace = 'enabled=on', end_markers_in_json=on,再执行目标 SQL,**立刻**查information_schema.optimizer_trace——延迟哪怕一条语句,就可能被覆盖
如何避免 JSON 被截断或只看到最后一条
optimizer_trace_max_mem_size 默认仅 1048576 字节(1MB),复杂 JOIN 或子查询极易触发 MISSING_BYTES_BEYOND_MAX_MEM_SIZE > 0,关键路径被砍掉;同时默认只存 1 条,多语句互相覆盖。
- 执行前先调大内存:
SET optimizer_trace_max_mem_size = 4194304(4MB) - 查上一条 trace:
SET optimizer_trace_offset = -2, optimizer_trace_limit = 1,再查视图 - 查连续 5 条(如存储过程内多个子查询):
SET optimizer_trace_offset = -5, optimizer_trace_limit = 5 - 每次分析前加
SET optimizer_trace = 'enabled=off'; SET optimizer_trace = 'enabled=on';,清旧记录防干扰
从 steps 里快速定位索引选择失败原因
别扫全量 JSON,重点翻 steps 数组里的 join_optimization 块:
-
condition_processing:看 WHERE 是否被重写(比如隐式类型转换导致索引失效) -
range_analysis → analyzing_range_alternatives:若显示"index": "PRIMARY"但"rows": 999999,说明优化器认为全表扫比走主键范围更快——大概率缺复合索引 -
considered_execution_plans:列出所有候选计划及其cost,对比“被选中”和“被放弃”的cost差值 -
ref_optimizer_key_uses块里若"using_index": false,得回溯前面condition_processing或rows_estimation看过滤率是否被严重高估
真正该盯住的三个 cost 字段在哪、怎么看
别只扫顶层 total_cost,它藏在深层嵌套里,真正影响决策的是相对值:
-
read_cost:引擎层读取成本(索引查找、回表、IO 预估) -
eval_cost:Server 层 WHERE 条件判断成本(行数 × 每行开销) -
prefix_cost:多表 JOIN 中到当前表为止的累计成本,比total_cost更能反映该表“贡献”
这些值单位是“随机 IO 次数”,不是毫秒;同一语句不同 plan 之间可比,跨语句不可比。路径通常是:steps → join_optimization → considered_execution_plans → [0] → cost。











