开启optimizer_trace前必须确认三件事:一是检查optimizer_trace_max_mem_size并建议设为10MB,二是启用end_markers_in_json=on,三是确保目标语句为SELECT/UPDATE/DELETE/EXPLAIN。

开启optimizer_trace前必须确认的三件事
它不是全局开关,也不是“开一次管所有”,而是会话级、单次生效的诊断工具。直接执行 SET SESSION optimizer_trace="enabled=on" 之前,先检查三件事:
-
SHOW VARIABLES LIKE 'optimizer_trace%'看当前optimizer_trace_max_mem_size值——默认 1MB,复杂查询容易被截断,建议临时设为10485760(10MB) -
end_markers_in_json=on必须同时启用,否则 JSON 中大括号难配对,读 Trace 时极易漏掉关键分支 - 确认你要分析的语句是
SELECT、UPDATE、DELETE或EXPLAIN——INSERT和REPLACE虽支持,但 Trace 内容极简,基本无决策细节
执行后立刻查 INFORMATION_SCHEMA.OPTIMIZER_TRACE 的正确姿势
执行目标 SQL 后,不能隔几秒再查 OPTIMIZER_TRACE 表——它的内容只保留最近一次优化结果,且每个会话独立。常见错误是:
- 在另一个客户端或新连接里查,返回空结果(因为不是同一个会话)
- 执行了两次目标 SQL,只查到第二次的 Trace(第一次被覆盖)
- 没加
\G直接SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE,导致 JSON 挤成一行难以定位"join_optimization"或"condition_processing"
正确做法:在同一会话中,执行完目标 SQL 立即运行 SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE\G。
重点盯住 "join_optimization" 里的成本估算差异
真正决定执行计划的,不是“有没有索引”,而是优化器算出来的 cost 数值。Trace 中该节点下的 "records"(预估行数)和 "cost"(I/O + CPU 成本)才是关键。
- 如果看到多个索引候选(如
"key": "idx_user_id"和"key": "idx_create_time"),对比它们的"cost"值——差 2 倍以上就说明优化器有明显倾向 -
"MISSING_BYTES_BEYOND_MAX_MEM_SIZE > 0意味着成本计算过程被截断,此时cost值不可信,必须调大optimizer_trace_max_mem_size重试 - 注意
"range_analysis"子节:它会显示“为什么没选某个索引”,常见原因是"rows": 1000000(预估全扫)远高于实际,本质是统计信息过期,需ANALYZE TABLE
关掉 optimizer_trace 不是可选项,是必做动作
它不写磁盘,但全程在内存里构建 JSON 结构,且每个会话独占一份缓冲区。长期开着会导致:
- 高并发下
optimizer_trace_max_mem_size总和吃光可用内存 - 即使没查
OPTIMIZER_TRACE表,只要开关开着,每次查询都多走一遍决策记录逻辑,QPS 下降 3%~8% -
SET SESSION optimizer_trace="enabled=off"必须显式执行,会话结束不会自动清理
最稳妥流程:开 → 执行一次 SQL → 查 Trace → 关。中间任何一步跳过,都可能让后续分析失真或拖慢线上服务。


















