EXPLAIN看不出索引没走的原因是因为它只显示最终执行计划,不记录优化器放弃候选索引的过程;真正原因需通过开启optimizer_trace并查看range_analysis等关键节点分析。

为什么EXPLAIN看不出索引没走的原因?
因为EXPLAIN只输出最终选定的执行计划,不记录优化器放弃其他候选计划的过程。比如你看到type=ALL、key=NULL,但完全不知道优化器是否考虑过idx_customer_id,也不知道它估算的成本比全表扫描高还是低——这些决策依据全在optimizer_trace里。
开启optimizer_trace必须配的三个参数
光设optimizer_trace = "enabled=on"不够,容易拿到截断或难读的结果:
-
end_markers_in_json = ON:让JSON每层末尾加注释(如/* range_optimizer */),否则大段嵌套括号根本对不上 -
optimizer_trace_max_mem_size = 1048576:默认1MB太小,复杂JOIN或子查询极易触发MISSING_BYTES_BEYOND_MAX_MEM_SIZE > 0,导致关键成本估算被砍掉 -
optimizer_trace_features = "greedy_search=on,range_optimizer=on,dynamic_range=on,repeated_subselect=on":关掉任意一项都可能漏掉索引选择或连接顺序的决策日志
从INFORMATION_SCHEMA.OPTIMIZER_TRACE里快速定位问题点
查完SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE\G后,别通读JSON。直接搜这几个关键词:
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
-
"range_analysis":看优化器是否识别出可用索引,以及它估算的rows和cost——如果这里显示possible_keys有索引但chosen_range_access_summary为空,说明范围扫描被否决了 -
"considered_execution_plans":列出所有被评估过的JOIN顺序和访问方式,对比各计划的cost值,找出“明明A计划cost更低却没选”的反常点 -
"analyzing_range_alternatives":重点看index_dives是否为false——如果是,说明优化器跳过了索引 dives(因eq_range_index_dive_limit限制),转而用统计信息粗估,可能导致误判
常见误操作和隐蔽坑
很多人开了optimizer_trace却看不到有效信息,问题往往出在:
- 在
EXPLAIN SELECT ...上开trace——没用。EXPLAIN不触发完整优化流程,必须执行真实SELECT或UPDATE等DML语句 - 用
SET GLOBAL optimizer_trace = "enabled=on"后去另一个会话查SQL——无效。optimizer_trace是会话级的,且只记录当前会话执行的语句 - 看到
"using_filesort": true就以为是ORDER BY导致的——不一定。先查"filesort_information"里的table字段,确认是不是驱动表没走索引,被迫把排序下推到临时表
真正耗时的从来不是JSON本身,而是你跳过"range_analysis"直接翻到结尾找"chosen_plan"——那只是结果,不是原因。

















