MySQL调优(三)——EXPLAIN 执行计划

目录

[一、EXPLAIN 核心概述](#一、EXPLAIN 核心概述)

[1.1 定义与作用](#1.1 定义与作用)

[1.2 适用范围](#1.2 适用范围)

[1.3 输出格式](#1.3 输出格式)

[二、EXPLAIN 输出字段全解析](#二、EXPLAIN 输出字段全解析)

[2.1 核心字段总览](#2.1 核心字段总览)

[2.2 字段详细说明(附实战案例)](#2.2 字段详细说明(附实战案例))

[2.2.1 id:查询执行顺序标识](#2.2.1 id:查询执行顺序标识)

[2.2.2 select_type:查询类型](#2.2.2 select_type:查询类型)

[2.2.3 table:表 / 临时表标识](#2.2.3 table:表 / 临时表标识)

[2.2.4 type:访问类型(核心指标)](#2.2.4 type:访问类型(核心指标))

[2.2.5 possible_keys & key:索引选择](#2.2.5 possible_keys & key:索引选择)

[2.2.6 key_len:索引长度(字节)](#2.2.6 key_len:索引长度(字节))

[2.2.7 ref:匹配对象](#2.2.7 ref:匹配对象)

[2.2.8 rows:预估扫描行数](#2.2.8 rows:预估扫描行数)

[2.2.9 filtered:过滤比例](#2.2.9 filtered:过滤比例)

[2.2.10 Extra:额外关键信息(性能陷阱)](#2.2.10 Extra:额外关键信息(性能陷阱))

[✅ 优质信号(无需优化)](#✅ 优质信号(无需优化))

三、实战案例分析

[3.1 案例 1:主键查询(最优性能)](#3.1 案例 1:主键查询(最优性能))

[3.2 案例 2:全表扫描(需优化)](#3.2 案例 2:全表扫描(需优化))

[3.3 案例 3:覆盖索引(最优优化)](#3.3 案例 3:覆盖索引(最优优化))

[3.4 案例 4:文件排序(需优化)](#3.4 案例 4:文件排序(需优化))

[四、EXPLAIN 进阶用法](#四、EXPLAIN 进阶用法)

[4.1 格式化输出](#4.1 格式化输出)

[4.2 分析实际执行情况](#4.2 分析实际执行情况)

[4.3 分区表分析](#4.3 分区表分析)

五、总结与优化原则

[5.1 核心优化原则](#5.1 核心优化原则)

[5.2 调优流程](#5.2 调优流程)


一、EXPLAIN 核心概述

1.1 定义与作用

EXPLAIN 是 MySQL 提供的 SQL 性能分析工具,用于模拟优化器执行 SQL 语句,展示查询执行计划(Execution Plan),核心作用包括:

  • 揭示 MySQL 如何选择索引联表顺序扫描行数
  • 定位慢 SQL 瓶颈(如全表扫描、临时表、文件排序);
  • 指导索引优化与 SQL 改写,是数据库调优的必备工具。

1.2 适用范围

支持 SELECTDELETEINSERTREPLACEUPDATE 语句,不真实执行 SQL,仅输出优化器的执行策略。

1.3 输出格式

默认输出为传统表格格式 ,也可通过 FORMAT=JSON/FORMAT=TREE 查看结构化 / 树形结构,便于复杂查询分析。

二、EXPLAIN 输出字段全解析

2.1 核心字段总览

字段名 核心含义 重要等级
id 查询执行顺序标识 ★★★★
select_type 查询类型(简单 / 子查询 / 联合) ★★★★
table 涉及的表 / 临时表 ★★★
type 访问类型(索引使用效率核心) ★★★★★
possible_keys 可选索引列表 ★★★
key 实际使用的索引 ★★★★★
key_len 实际使用的索引长度(字节) ★★★★
ref 与索引匹配的列 / 常量 ★★★
rows 预估扫描行数 ★★★★
filtered 过滤后剩余数据比例 ★★★
Extra 额外关键信息(性能陷阱) ★★★★★

2.2 字段详细说明(附实战案例)

2.2.1 id:查询执行顺序标识
  • 含义 :查询中每个 SELECT 子句的唯一编号,决定执行优先级。

  • 规则

    1. 数值越大 ,执行越先(子查询 / 派生表优先执行);
    2. 数值相同 ,按从上到下顺序执行;
    3. NULL:表示该行为联合查询结果(如 <union M,N>),需合并 id 为 M、N 的结果。
  • 示例

    sql 复制代码
    -- 子查询+联合查询示例
    EXPLAIN 
    SELECT * FROM t1 WHERE id = 1
    UNION
    SELECT * FROM t2 WHERE id IN (SELECT id FROM t3);

    执行顺序:id=3(子查询)→ id=2(t2 查询)→ id=1(t1 查询)→ 合并 union 结果。

2.2.2 select_type:查询类型
取值 含义 性能提示
SIMPLE 简单查询(无 子查询 / UNION) ✅ 基础查询,性能稳定
PRIMARY 最外层查询(含子查询 / UNION 时) -
SUBQUERY 子查询中的第一个 SELECT(非相关子查询) ⚠️ 可能存在性能损耗,建议优化为 JOIN
DERIVED 派生表(FROM 子句中的子查询) ⚠️ 会生成临时表,大数据量下性能差
UNION UNION 中第二个及后续的 SELECT ⚠️ 需合并结果,额外开销
UNION RESULT UNION 合并后的结果集 无实际表,table 列显示 <union M,N>
DEPENDENT SUBQUERY 相关子查询(依赖外层查询结果) ❌ 性能极差,优先改写为 JOIN
MATERIALIZED 子查询结果物化(生成临时表) ✅ 优化相关子查询的替代方案
2.2.3 table:表 / 临时表标识
  • 显示当前行对应的表名别名
  • 特殊值:
    • <derivedN>:id 为 N 的派生表;
    • <subqueryN>:id 为 N 的物化子查询结果;
    • <union M,N>:合并 id 为 M、N 的 UNION 结果。
2.2.4 type:访问类型(核心指标)

决定索引使用效率的最关键字段 ,性能从优到差排序如下,需尽量达到 range 及以上级别:

取值 含义 适用场景 性能等级
system 表中仅有 1 行数据 系统表 最优
const 主键 / 唯一索引等值查询,最多匹配 1 行 WHERE id=1(id 为主键) 极优
eq_ref 联表时,被驱动表用主键 / 唯一索引等值匹配 联查询(JOIN ... ON 主键列 极优
ref 普通索引等值查询,可匹配多行 WHERE name='张三'(name 建索引)
range 索引范围查询(>、<、BETWEEN、IN) WHERE id BETWEEN 1 AND 100
index 全索引扫描(遍历整个索引树) 需覆盖索引,但无过滤条件
ALL 全表扫描(遍历所有数据行) 无索引 / 小表临时查询 最差(大表禁用)
2.2.5 possible_keys & key:索引选择
  • possible_keys :MySQL 评估的可选索引列表,仅为参考,不代表实际使用;
  • key :MySQL 实际选用 的索引,为 NULL 则表示未使用索引(需优化)。
  • 关键判断
    • possible_keys 非空但 key 为空,说明索引未命中,需检查索引设计 / SQL 写法;
    • 联合索引需遵循最左匹配原则,否则索引失效。
2.2.6 key_len:索引长度(字节)
  • 表示 MySQL 实际使用的索引长度越短越好
  • 核心用途:判断联合索引用到了几列
  • 常见数据类型长度参考(utf8mb4 编码):
数据类型 长度(字节)
INT 4
BIGINT 8
VARCHAR(100) 100×4 + 2 = 402
DATETIME 5(5.6+ 版本)/ 8(旧版本)
CHAR(10) 10×4 = 40
  • 示例 :联合索引 idx_a_b_c (a INT, b VARCHAR(10), c INT)
    • key_len=4:仅使用第 1 列(a);
    • key_len=406:使用第 1、2 列(a + b);
    • key_len=410:使用全部 3 列。
2.2.7 ref:匹配对象
  • 表示与索引列等值匹配 的对象,可为:
    • 常量(如 '张三'1);
    • 其他表的列(如 db.t2.id);
    • NULL:非等值匹配或无匹配。
2.2.8 rows:预估扫描行数
  • MySQL 基于统计信息估算 的需扫描行数,数值越小越好
  • 与实际行数可能存在偏差,可通过 ANALYZE TABLE 更新统计信息;
  • 优化目标:大表场景下,尽量将 rows 控制在可接受范围(如万级以内)。
2.2.9 filtered:过滤比例
  • 表示存储引擎返回数据后,经 WHERE 条件过滤后剩余的比例(%);
  • 数值越高 ,说明索引筛选能力越强,查询效率越高;
  • 示例:rows=1000filtered=10% → 预估最终返回 100 行。
2.2.10 Extra:额外关键信息(性能陷阱)

最易被忽略但最关键的字段,直接暴露 SQL 性能问题,常见取值如下:

✅ 优质信号(无需优化)
取值 含义
Using index 覆盖索引(索引包含查询所需所有字段,无需回表),性能最优
Using where 已通过 WHERE 条件过滤数据

⚠️ 优化信号(需关注)

取值 含义 优化建议
Using index condition 索引条件下推(ICP),减少回表 检查联合索引列顺序,提升筛选效率
Using temporary 生成临时表(常见于 GROUP BY/UNION/DISTINCT) 优化分组 / 排序逻辑,添加合适索引
Using filesort 文件排序(未使用索引排序) 为排序字段建索引,避免全表排序

❌ 危险信号(必须优化)

取值 含义 优化建议
Impossible WHERE WHERE 条件永远为假 检查 SQL 逻辑,删除无效查询
Select tables optimized away 无需访问表(如聚合函数查询) 无需处理(性能最优)

三、实战案例分析

3.1 案例 1:主键查询(最优性能)

sql 复制代码
EXPLAIN SELECT * FROM user WHERE id = 1;
id select_type table type key rows Extra
1 SIMPLE user const PRIMARY 1 NULL
  • 分析:type=const(主键等值查询),key=PRIMARY,rows=1,性能最优。

3.2 案例 2:全表扫描(需优化)

sql 复制代码
EXPLAIN SELECT * FROM user WHERE name = '张三';
id select_type table type key rows Extra
1 SIMPLE user ALL NULL 1000 Using where
  • 分析 :type=ALL(全表扫描),key=NULL,需为 name 字段创建索引。

3.3 案例 3:覆盖索引(最优优化)

sql 复制代码
-- 先创建索引:CREATE INDEX idx_name_age ON user(name, age);
EXPLAIN SELECT name, age FROM user WHERE name = '张三';
id select_type table type key Extra
1 SIMPLE user ref idx_name_age Using index
  • 分析:Extra=Using index(覆盖索引),无需回表,性能最优。

3.4 案例 4:文件排序(需优化)

id select_type table type Extra
1 SIMPLE user ALL Using filesort
  • 分析 :Extra=Using filesort,需为 create_time 建索引,避免全表排序。

四、EXPLAIN 进阶用法

4.1 格式化输出

sql 复制代码
-- JSON 格式(适合复杂查询)
EXPLAIN FORMAT=JSON SELECT * FROM user WHERE id=1;

-- 树形格式(直观展示执行顺序)
EXPLAIN FORMAT=TREE SELECT * FROM user u JOIN order o ON u.id=o.user_id;

4.2 分析实际执行情况

EXPLAIN ANALYZE真实执行 SQL,并输出实际执行时间、扫描行数,对比预估与实际值的偏差:

sql 复制代码
EXPLAIN ANALYZE SELECT * FROM user WHERE name='张三';

4.3 分区表分析

添加 PARTITIONS 关键字,显示命中的分区信息:

sql 复制代码
EXPLAIN PARTITIONS SELECT * FROM user_part WHERE id=1;

五、总结与优化原则

5.1 核心优化原则

  1. type 达标 :尽量达到 range 及以上,避免 ALL
  2. 索引命中 :保证 key 非空,遵循联合索引最左匹配原则;
  3. 减少回表:优先使用覆盖索引(Using index);
  4. 消除额外开销 :避免 Using temporaryUsing filesort

5.2 调优流程

  1. EXPLAIN 分析慢 SQL;
  2. 重点关注 typekeyExtra 字段;
  3. 针对性优化(建索引、改写 SQL、调整表结构);
  4. 再次用 EXPLAIN 验证优化效果。

EXPLAIN 是 MySQL 调优的入门钥匙,掌握其核心字段含义,能快速定位 SQL 性能瓶颈。在实际开发中,需结合业务场景灵活运用,避免过度优化。

相关推荐
元拓数智1 小时前
从 SQL 到自然语言:Arilink 语义治理与智能查询平台深度解析
数据库·sql·自然语言处理·智能问数
2401_887724501 小时前
怎样使用Navicat高级特权进行从备份中提取单表数据_企业数据保护.txt
jvm·数据库·python
其实防守也摸鱼2 小时前
dirsearch安装与配置:新手入门指南
网络·数据库·学习·学习方法·目录扫描·虚拟机工具
2402_854808372 小时前
如何用 stopPropagation 阻止事件冒泡防止触发父级回调
jvm·数据库·python
2301_782659182 小时前
Golang怎么实现方法集与接口的匹配_Golang如何理解值类型和指针类型实现接口的区别【详解】
jvm·数据库·python
2301_814809862 小时前
PHP源码开发用二手硬件划算吗_性价比与稳定性权衡【操作】
jvm·数据库·python
2301_782659182 小时前
C#怎么操作PostgreSQL数据库 C#如何用Npgsql连接和操作PostgreSQL进行数据读写【数据库】
jvm·数据库·python
2401_897190552 小时前
CSS如何处理层级混乱问题_利用z-index与Stacking Context原理
jvm·数据库·python
m0_748839492 小时前
Golang怎么实现配置校验_Golang如何在启动时检查必填配置项是否缺失【技巧】
jvm·数据库·python