Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > MySQL优化器追踪

MySQL优化器追踪(Optimizer Trace)的使用小结

作者:索java

MySQL OptimizerTrace 是用于分析查询优化器决策过程的工具,通过输出JSON格式的详细执行信息,帮助开发者理解优化器如何选择执行计划,感兴趣的可以了解一下

1.1 Optimizer Trace简介

1.1 引入背景

在MySQL中,查询优化器负责解析SQL语句,并根据成本估算选择最优执行计划。然而,对于复杂查询,有时优化器的决策过程难以理解,尤其是出现:

为了解决这些问题,MySQL从5.6版本引入了 Optimizer Trace 功能,用于追踪优化器的内部决策过程,以 JSON 格式输出详细执行信息。

官方文档链接
MySQL 8.0 Reference Manual - Optimizer Trace

1.2 Optimizer Trace 与 EXPLAIN 的区别

特性EXPLAINOptimizer Trace
输出内容查询执行计划优化器内部决策详细信息(JSON)
适用场景查看执行顺序、索引使用情况分析优化器为何选择特定执行计划
输出格式表格JSON
层级信息平面层次化(join_preparation、join_optimization等)
调试价值较低高,可定位优化器决策逻辑问题

可以把 EXPLAIN 理解为“最终计划的快照”,而 Optimizer Trace 则是“优化器的思考过程记录”。

1.3 适用场景

1.4 示例代码:开启 Optimizer Trace 并执行简单查询

环境:MySQL 8.0,单机测试

-- 开启 Optimizer Trace
SET optimizer_trace="enabled=on";
 
-- 创建测试表
CREATE TABLE employees (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    department_id INT,
    INDEX idx_dept(department_id)
);
 
INSERT INTO employees VALUES
(1,'Alice',1),
(2,'Bob',2),
(3,'Charlie',1);
 
-- 执行简单查询
SELECT * FROM employees WHERE department_id = 1;
 
-- 查看 Optimizer Trace
SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE\G
 
-- 关闭 Optimizer Trace
SET optimizer_trace="enabled=off";

预期结果

2 工作原理与核心价值

2.1 Optimizer Trace 的工作阶段

Optimizer Trace 会在查询优化阶段对优化器的决策流程进行追踪,主要包括以下几个阶段(Stage):

join_preparation

功能:初始化 JOIN 查询的基本信息

内容:

作用:为后续优化器选择最优 JOIN 顺序做准备

join_optimization

功能:选择最优连接顺序和访问方法

内容:

作用:记录优化器如何计算每种执行方案的代价并选择最优方案

final_plan

功能:生成最终执行计划

内容:

作用:把优化器的“思考结果”形成可执行计划

注意:每个阶段都会输出 JSON 结构,包含详细的决策信息,包括成本(cost)、扫描行数(rows)、使用索引(access_method)等。

2.2 JSON 输出结构解析

Optimizer Trace 输出主要字段说明:

{
  "trace": [
    {
      "step": 1,
      "phase": "join_preparation",
      "table": "employees",
      "access_method": "ALL",
      "rows": 3
    },
    {
      "step": 2,
      "phase": "join_optimization",
      "join_order": ["employees"],
      "cost": 0.03,
      "chosen_access": "index(idx_dept)"
    },
    {
      "step": 3,
      "phase": "final_plan",
      "plan": [
        {
          "table": "employees",
          "access_method": "index",
          "key": "idx_dept",
          "rows_examined": 2
        }
      ]
    }
  ]
}

字段说明

字段说明
step优化器执行步骤序号
phase优化阶段(join_preparation、join_optimization、final_plan)
table当前操作的表
access_method表访问方式(ALL 表示全表扫描,index 表示索引访问)
rows预估扫描行数
join_order优化器尝试的表连接顺序
cost优化器对执行方案的代价估算
chosen_access优化器最终选择的访问方式
plan最终执行计划详细信息
key使用的索引
rows_examined实际预计扫描行数

通过这个 JSON 输出,开发者可以 逐步追踪优化器的每个决策,并分析为什么某些索引未被使用、JOIN 顺序为何不合理。

2.3 Optimizer Trace 的核心价值

可视化优化器决策过程

对比 EXPLAIN,Trace 不仅展示最终计划,还展示选择路径和成本比较。

定位索引选择问题

能够分析为什么优化器没有选择期望索引,便于调整统计信息或优化 SQL。

分析成本估算偏差

Trace 提供每一步的成本和行数估算,方便对比实际执行情况。

调优验证

调整索引或 SQL 后,可通过 Trace 验证优化器是否采纳建议。

结合 EXPLAIN 使用

EXPLAIN 展示最终计划,Trace 展示优化器思路,二者结合可以完整理解查询执行逻辑。

2.4 示例代码:分析复杂查询的 Trace 输出

环境:MySQL 8.0

-- 开启 Trace
SET optimizer_trace='enabled=on';
 
-- 创建测试表
CREATE TABLE departments (
    id INT PRIMARY KEY,
    name VARCHAR(50)
);
 
CREATE TABLE employees (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    department_id INT,
    INDEX idx_dept(department_id)
);
 
INSERT INTO departments VALUES (1,'HR'),(2,'IT');
INSERT INTO employees VALUES (1,'Alice',1),(2,'Bob',2),(3,'Charlie',1);
 
-- 执行多表 JOIN 查询
SELECT e.name, d.name
FROM employees e
JOIN departments d ON e.department_id = d.id
WHERE d.id = 1;
 
-- 查看 Trace
SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE\G
 
-- 关闭 Trace
SET optimizer_trace='off';

分析步骤

3 配置参数详解

Optimizer Trace 依赖 MySQL 系统变量来控制开启、输出内容和调试精度。主要参数包括:

3.1 optimizer_trace

作用:控制 Optimizer Trace 功能开关

类型:字符串(枚举 enabled=on|off 或 JSON 配置)

默认值enabled=off

修改方法

-- 开启 Trace
SET optimizer_trace='enabled=on';
 
-- 关闭 Trace
SET optimizer_trace='enabled=off';

JSON 形式

SET optimizer_trace='{
    "enabled": true,
    "trace_objects": "all",
    "max_elements": 1000
}';

trace_objects:指定追踪对象,可选 alloptimizer

max_elements:JSON 输出中最大元素数,超过会被截断

3.2 optimizer_trace_features

作用:控制 Trace 输出的内容粒度

类型:字符串(逗号分隔)

默认值"basic"

可选值

修改方法

SET optimizer_trace_features='all'; 

3.3 max_optimizer_trace_elements

作用:限制 Trace 输出的最大元素数量

类型:整数

默认值1000

修改方法

SET max_optimizer_trace_elements=2000; 

影响:如果 Trace 输出超过限制,JSON 会被截断,分析不完整。

3.4 注意事项

系统级变量 vs 会话级变量

与其他参数的冲突

对性能的影响

3.5 示例:调整参数并观察 Trace 输出差异

环境:MySQL 8.0,测试库

-- 创建测试表
CREATE TABLE test_employees (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    dept_id INT,
    INDEX idx_dept(dept_id)
);
 
INSERT INTO test_employees VALUES (1,'Alice',1),(2,'Bob',2),(3,'Charlie',1);
 
-- 开启 Trace 并设置输出全部详细信息
SET optimizer_trace='enabled=on';
SET optimizer_trace_features='all';
SET max_optimizer_trace_elements=5000;
 
-- 执行查询
SELECT * FROM test_employees WHERE dept_id=1;
 
-- 查看 Trace
SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE\G
 
-- 调整参数只输出 basic
SET optimizer_trace_features='basic';
 
-- 再次执行查询查看差异
SELECT * FROM test_employees WHERE dept_id=1;
 
-- 关闭 Trace
SET optimizer_trace='enabled=off';

预期效果

3.6 小结

4 使用方法与调试流程

Optimizer Trace 的使用主要包括四个步骤:开启 Trace → 执行查询 → 查看 Trace → 分析与验证。下面逐步讲解。

4.1 步骤 1:开启 Optimizer Trace

基本开启方法(会话级):

SET optimizer_trace='enabled=on';

指定输出详细程度(可选):

SET optimizer_trace_features='all';  -- 输出所有信息,包括 I/O 和内存估算
SET max_optimizer_trace_elements=5000;  -- 避免复杂查询输出被截断

关闭 Trace

SET optimizer_trace='enabled=off';

注意:生产环境中建议仅在调试场景开启,以避免性能开销。

4.2 步骤 2:执行需要分析的查询

示例:MySQL 8.0

CREATE TABLE employees (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    department_id INT,
    INDEX idx_dept(department_id)
);
 
CREATE TABLE departments (
    id INT PRIMARY KEY,
    name VARCHAR(50)
);
 
INSERT INTO departments VALUES (1,'HR'),(2,'IT');
INSERT INTO employees VALUES (1,'Alice',1),(2,'Bob',2),(3,'Charlie',1);
 
-- 执行复杂 JOIN 查询
SELECT e.name, d.name
FROM employees e
JOIN departments d ON e.department_id = d.id
WHERE d.id = 1;

4.3 步骤 3:查看 Trace 输出

查询 Trace:

SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE\G; 

输出分析:

关键字段:

结合 JSON 格式化工具,可清晰看到优化器每一步决策过程。

4.4 步骤 4:分析与验证

与 EXPLAIN 对比

EXPLAIN SELECT e.name, d.name
FROM employees e
JOIN departments d ON e.department_id = d.id
WHERE d.id = 1\G;

分析决策过程

查看 join_optimization 阶段:

查看 final_plan 阶段:

调试索引选择错误示例

假设优化器未使用期望索引:

-- 查询未使用索引
SELECT * FROM employees WHERE department_id=1;
 
-- Trace 显示 access_method=ALL(全表扫描)
-- 分析可能原因:
-- 1. 索引统计信息不准确
-- 2. 表行数过少,优化器认为全表扫描成本低
-- 调整统计信息
ANALYZE TABLE employees;
 
-- 再次执行查询,观察 Trace 输出是否选择 idx_dept 索引

通过对比 Trace 输出,可以清楚看到优化器为何选择或忽略某个索引,从而指导索引优化或 SQL 调整。

4.5 小结

5 典型场景分析

场景1:索引选择错误

5.1.1 问题描述

示例查询(MySQL 8.0):

SELECT * FROM employees WHERE department_id = 1; 

5.1.2 Trace 输出分析

{
  "trace": [
    {
      "phase": "join_preparation",
      "table": "employees",
      "access_method": "ALL",
      "rows": 3
    },
    {
      "phase": "final_plan",
      "plan": [
        {
          "table": "employees",
          "access_method": "ALL",
          "rows_examined": 3
        }
      ]
    }
  ]
}

分析:

access_method=ALL 表示全表扫描

优化器认为全表扫描成本低于索引访问,可能由于:

5.1.3 调优建议

-- 更新表统计信息
ANALYZE TABLE employees;
 
-- 再次执行查询,观察 Trace 输出
SELECT * FROM employees WHERE department_id=1;
 
-- 预期 Trace 输出:
-- access_method=index
-- 使用 idx_dept 索引

场景2:成本估算偏差

5.2.1 问题描述

示例:

SELECT * FROM employees e
JOIN departments d ON e.department_id = d.id
WHERE d.id IN (1,2);

5.2.2 Trace 输出分析

{
  "phase": "join_optimization",
  "join_order": ["departments","employees"],
  "cost": 0.05,
  "chosen_access": "index(idx_dept)"
}

分析:

5.2.3 调优建议

-- 更新表统计信息
ANALYZE TABLE employees;
ANALYZE TABLE departments;
 
-- 调整查询逻辑或提示优化器
SELECT /*+ JOIN_ORDER(d,e) */ e.name, d.name
FROM employees e
JOIN departments d ON e.department_id = d.id
WHERE d.id IN (1,2);

场景3:子查询优化问题

5.3.1 问题描述

示例:

SELECT name
FROM employees
WHERE department_id IN (
    SELECT id FROM departments WHERE name='HR'
);

5.3.2 Trace 输出分析

{
  "phase": "join_optimization",
  "subquery": {
    "type": "IN",
    "rows": 1,
    "access_method": "ALL"
  },
  "final_plan": {
    "employees": {"access_method": "index", "rows_examined": 2}
  }
}

分析:

5.3.3 调优建议

-- 转换为 JOIN
SELECT e.name
FROM employees e
JOIN departments d ON e.department_id = d.id
WHERE d.name='HR';

Trace 输出应显示:

场景4:临时表/文件排序问题

5.4.1 问题描述

示例:

SELECT department_id, COUNT(*) 
FROM employees
GROUP BY department_id
ORDER BY COUNT(*) DESC;

5.4.2 Trace 输出分析

{
  "phase": "final_plan",
  "plan": [
    {
      "table": "employees",
      "access_method": "index",
      "using_temporary": true,
      "using_filesort": true
    }
  ]
}

分析:

5.4.3 调优建议

-- 使用覆盖索引或调整查询
CREATE INDEX idx_dept_count ON employees(department_id);
 
-- 执行查询并观察 Trace 输出
-- 预期不再使用文件排序,性能提升

5.5 小结

Optimizer Trace 能清晰展示优化器每一步决策

通过 Trace 可定位:

调优流程:

到此这篇关于MySQL优化器追踪(Optimizer Trace)的使用小结的文章就介绍到这了,更多相关MySQL优化器追踪内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

您可能感兴趣的文章:
阅读全文