MySQL 优化器(OPTIMIZER_TRACE):解析查询计划的内部逻辑
MySQL 的查询优化器是决定 SQL 执行效率的核心组件,它负责生成最优执行计划(如选择索引、连接方式等)。通过可插拔的 OPTIMIZER_TRACE 工具,我们可以追踪优化器的内部决策过程,了解其如何解析 SQL、估算行数、选择执行计划,从而针对性地优化 SQL。
OPTIMIZER_TRACE 基础配置
查看优化器状态
默认情况下,OPTIMIZER_TRACE 是关闭的,可通过以下命令查看状态:
1 | show variables like 'optimizer_trace'; |
输出示例:
1 | +-----------------+-----------------------+ |
enabled=off:未开启追踪;enabled=on表示开启。one_line=off:JSON 结果格式化显示;one_line=on表示单行紧凑显示。
开启优化器追踪
通过 set session 命令临时开启当前会话的追踪功能(仅对当前会话有效):
1 | -- 开启追踪,JSON 结果单行显示,包含结束标记 |
配置追踪内存大小
优化器追踪结果会占用内存,可通过以下命令查看和调整最大内存限制:
1 | -- 查看当前限制(默认 16KB) |
查看优化器追踪结果
执行目标 SQL 并获取 trace
执行需要分析的 SQL 后,通过 INFORMATION_SCHEMA.OPTIMIZER_TRACE 表查看追踪结果:
1 | -- 示例:执行需要分析的 SQL |
注意:
- 必须在同一会话中执行 SQL 和查看 trace(每个 session 仅追踪自己的语句)。
- 若分步执行(先执行 SQL,再查 trace),可能导致 trace 为空,需同时选中两条语句执行(如 Navicat 中)。
trace 结果结构解析
trace 结果以 JSON 格式展示,包含优化器处理 SQL 的完整步骤,核心分为 steps 数组,每个步骤对应优化器的一个阶段。以下是关键阶段的解析:
阶段 1:join_preparation(查询准备)
该阶段主要对 SQL 进行语法解析、扩展和转换,生成优化器可处理的格式。
示例片段:
1 | { |
作用:
- 展开别名、替换子查询(如将
IN (子查询)转为EXISTS,优化执行效率)。 - 生成 “扩展查询”(
expanded_query),便于后续优化。
阶段 2:join_optimization(查询优化)
该阶段是优化器的核心,包含条件处理、表依赖分析、行数估算、执行计划选择等关键步骤。
(1)condition_processing(条件处理)
对 WHERE、HAVING 等条件进行转换和简化(如常量传播、等式传递)。
示例片段:
1 | { |
(2)table_dependencies(表依赖分析)
分析查询涉及的表及其依赖关系,确认是否存在关联或子查询依赖。
示例片段:
1 | { |
(3)rows_estimation(行数估算)
优化器估算每个表需要扫描的行数,是选择索引的关键依据(行数越少,成本越低)。
示例片段:
1 | { |
作用:
- 对比全表扫描和索引扫描的成本(
cost),选择成本更低的方式。 - 若索引扫描成本低于全表扫描,优化器会优先选择索引。
(4)considered_execution_plans(执行计划选择)
优化器评估多种可能的执行计划,选择成本最低的方案。
示例片段:
1 | { |
作用:
- 展示优化器对不同执行方式(全表扫描、索引扫描等)的成本评估。
- 最终选择成本最低的计划(
chosen: true)。
(5)clause_processing(子句处理)
对 GROUP BY、ORDER BY 等子句进行优化,如判断是否可利用索引排序 / 分组。
示例片段:
1 | { |
阶段 3:join_explain(执行计划生成)
最终生成 EXPLAIN 语句可展示的执行计划,标志优化过程结束。
OPTIMIZER_TRACE 的实用场景
- 分析索引未被使用的原因:
若预期使用索引但实际未使用,通过rows_estimation查看优化器估算的索引扫描成本是否高于全表扫描(可能因索引选择性低导致)。 - 优化子查询性能:
在join_preparation阶段查看子查询是否被转换(如IN转EXISTS),若转换后效率仍低,可手动改写子查询为JOIN。 - 理解分组 / 排序的成本:
在considered_execution_plans中查看sort_cost(排序成本),若成本过高,可通过添加复合索引避免排序(Using filesort)。
注意事项
- 性能影响:
开启OPTIMIZER_TRACE会增加 MySQL 负担(追踪过程消耗 CPU 和内存),仅在调试时开启,生产环境建议关闭。 - 结果大小限制:
若 trace 结果超过optimizer_trace_max_mem_size,会被截断,需适当调大该参数(但不宜过大)。 - 会话隔离:
每个 session 仅能查看自己执行的 SQL 的 trace,无法跨会话查看
v1.3.10