开OPTIMIZER_TRACE不等于能看到完整决策链——它默认只存当前会话最后一条语句的JSON快照且易因1MB内存限制被截断;必须同会话先设enabled=on和end_markers_in_json=on,再执行真实SELECT/UPDATE,调大optimizer_trace_max_mem_size,关闭autocommit,并清旧trace才能获取有效信息。

开 OPTIMIZER_TRACE 不等于能看到完整决策链——它默认只存当前会话最后一条语句的 JSON 快照,且极易因 1MB 内存限制被截断。必须手动调参、严格同会话执行、逐条验证才能拿到有效信息。
为什么 SET optimizer_trace = "enabled=on" 后查不到 TRACE 或内容为空
这不是权限或语法问题,而是追踪逻辑没对上:
- 必须在同一个会话里:先
SET SESSION optimizer_trace = "enabled=on", end_markers_in_json=on,再执行真实SELECT/UPDATE(EXPLAIN不触发 trace) - 语句报错(如表不存在、列名错)会导致优化器跳过完整优化阶段,
TRACE字段为空或只有极简结构 -
INSUFFICIENT_PRIVILEGES = 1表示你对语句中涉及的表/视图没有SELECT权限,trace 强制清空 - 如果
autocommit = 1,SELECT执行完立刻提交,trace 内容可能被清掉;建议先SET autocommit = 0,查完再ROLLBACK
如何避免 JSON 截断和多语句覆盖
optimizer_trace_max_mem_size 默认仅 1048576 字节(1MB),复杂 JOIN 或含子查询的语句很容易触发 MISSING_BYTES_BEYOND_MAX_MEM_SIZE > 0,关键路径被砍掉:
解析 MySQL 8.0 的 DDL 脚本,并将表结构导出为 Excel 格式。适用于用户需将 MySQL 的 `CREATE TABLE` 语句转换为结构化的 Excel 文件场景,导出内容包含字段名、数据类型、长度及精度等信息。支持多张表的批量处理,每张表对应 Excel 中的一个独立工作表。适用于数据库文档编写、数据字典生成及模式分析等任务。
- 立即调大内存:
SET SESSION optimizer_trace_max_mem_size = 4194304(4MB) - 查上一条 trace:
SET SESSION optimizer_trace_offset = -2, optimizer_trace_limit = 1,再查information_schema.OPTIMIZER_TRACE - 查连续多条(比如存储过程里 5 个子查询):
SET SESSION optimizer_trace_offset = -5, optimizer_trace_limit = 5 - 每次分析前加
SET SESSION optimizer_trace = "enabled=off";SET SESSION optimizer_trace = "enabled=on",清掉旧记录防干扰
怎么从 TRACE 里定位“索引明明存在却没被选中”的硬证据
Trace 不会直接说“这个索引被忽略了”,但会在不同节点留下线索:
- 看
steps → join_optimization → range_analysis → potential_range_indices:如果某个索引根本没出现在这个数组里,说明它连候选资格都没拿到(常见于类型不匹配、函数包裹、隐式转换) - 看
condition_processing节点:若发现 WHERE 条件被重写成"original_condition": "CAST(status AS CHAR) = '1'",就暴露了隐式转换导致索引失效 - 对比
table_scan和各索引的cost:如果idx_create_time_status的cost是 120000,而table_scan的cost是 98000,说明优化器认为“走索引+回表”更贵——往往因为统计信息陈旧,或row_evaluate_cost参数偏高
真正容易被忽略的是:哪怕 trace 显示某索引的 rows 很小,只要它的 cost 高于全表扫描,它就不会被选中;而 cost 受 read_random_next_cost、key_compare_cost 等隐藏参数影响,不是纯靠索引字段顺序能推出来的。

















