MySQL EXPLAIN 详解与优化指南

EXPLAIN 是 SQL 性能优化的关键工具,它展示了 MySQL 如何执行一条 SQL 语句。通过分析它的结果,你可以找出查询的瓶颈并进行优化。

下面我将对 EXPLAIN 进行详细解析。

一、什么是 EXPLAIN?

EXPLAIN 关键字可以放在 SELECTDELETEINSERTREPLACEUPDATE 语句之前,MySQL 会返回该语句的执行计划,而不是真正执行它。

执行计划揭示了 MySQL 优化器决定如何访问表、使用哪些索引、表之间的连接方式等信息。

基本语法:

sql

复制代码
EXPLAIN your_sql_statement;
-- 例如
EXPLAIN SELECT * FROM users WHERE name = 'John';

在 MySQL 8.0 之后,推荐使用 EXPLAIN FORMAT=TRADITIONAL(默认格式),或者更详细的 EXPLAIN ANALYZE(MySQL 8.0.18+ 引入,会实际执行查询并给出更精确的分析)。


二、EXPLAIN 输出列详解

执行 EXPLAIN 后,你会得到一个包含多列的结果集。以下是这些列的含义,其中前几列(type, key, rows, Extra)最为重要

列名 描述 重要性
id 查询中每个 SELECT 子句的唯一标识符。如果相同,则按顺序执行;如果不同,id 越大优先级越高(先执行)。
select_type 查询的类型,如 SIMPLE, PRIMARY, SUBQUERY, DERIVED 等。
table 正在访问的表名。
partitions 匹配的分区,非分区表则为 NULL。
type 连接类型访问类型 。这是衡量查询性能的最关键指标之一。
possible_keys 查询中可能用到的索引。
key 查询中实际决定使用的索引。为 NULL 则表示未使用索引。
key_len 使用的索引键的长度。可用于判断是否充分利用了复合索引。
ref 显示索引的哪一列被使用了,通常是一个常量(const)或另一个表的列名。
rows MySQL 估计为了找到所需的行而需要读取的行数。这个值越小越好。
filtered 表示存储引擎返回的数据在服务器层过滤后,剩余多少比例满足查询条件。理想是 100。
Extra 包含不适合在其他列显示的额外信息,非常多的重要信息在这里。

三、核心列深度解析

1. type(访问类型)

性能从最优到最差排序如下:

  • system > const > eq_ref > ref > range > index > ALL

  • system:表只有一行记录(等于系统表),是 const 类型的特例。

  • const :通过索引一次就找到了,用于比较 主键索引唯一索引 与常数值。速度极快。

    sql

    复制代码
    EXPLAIN SELECT * FROM users WHERE id = 1;
    -- `id` 是主键
  • eq_ref :在连接查询时,使用 主键唯一非空索引 进行关联。对于来自前表的每一行,从本表中只返回一行。这是除了 systemconst 之外最好的连接类型。

    sql

    复制代码
    EXPLAIN SELECT * FROM users u 
    JOIN orders o ON u.id = o.user_id;
    -- `o.user_id` 是 `users` 表的主键 `id` 的外键,并且是唯一索引
  • ref :使用 非唯一性索引 进行扫描,返回匹配某个单独值的所有行。

    sql

    复制代码
    EXPLAIN SELECT * FROM users WHERE name = 'John';
    -- `name` 字段上有一个普通索引(非唯一)
  • range :只检索给定范围的行,使用一个索引来选择行。关键运算符是 BETWEEN><IN 等。

    sql

    复制代码
    EXPLAIN SELECT * FROM users WHERE id > 10;
    EXPLAIN SELECT * FROM users WHERE id IN (1, 2, 3);
  • index全索引扫描。遍历整个索引树来查找数据,比 ALL 快一点,因为索引文件通常比数据文件小。

    sql

    复制代码
    EXPLAIN SELECT id FROM users;
    -- 查询的列 `id` 正好是索引的一部分,直接从索引中读取,无需回表
  • ALL全表扫描 。性能最差,意味着 MySQL 会遍历整张表来找到匹配的行。必须优化

2. rows(预估行数)

这不是查询结果的行数,而是 MySQL 为了找到目标记录,预估需要扫描多少行。这是一个基于统计信息的预估值。这个值越小越好,说明查询效率高。

3. Extra(额外信息)

这里包含大量细节,是判断查询质量的另一个关键。

  • Using index覆盖索引。查询的列都包含在索引中,无需回表查询数据行。性能极佳。

    sql

    复制代码
    -- 假设在 `name` 和 `age` 上有一个复合索引 (name, age)
    EXPLAIN SELECT name, age FROM users WHERE name = 'John';
  • Using where :表示在存储引擎返回行后,MySQL 服务器层再次进行了过滤。如果 typeALLindex,并且出现 Using where,通常意味着性能不佳。

  • Using temporary :MySQL 需要创建一张临时表来处理查询。常见于 GROUP BYORDER BY 的子句涉及不同列时。需要优化

  • Using filesort :MySQL 无法使用索引对结果进行排序,需要额外的排序步骤。如果数据量大,会非常消耗资源。需要优化

  • Using join buffer:表示连接查询时,没有使用索引,需要用到连接缓冲区。通常意味着连接字段上没有索引。


四、实战分析示例

假设我们有两张表:

users

  • id (主键)

  • name (有普通索引)

  • email

orders

  • id (主键)

  • user_id (外键,关联 users.id,有索引)

  • amount

查询1:简单的等值查询

sql

复制代码
EXPLAIN SELECT * FROM users WHERE name = 'Alice';

可能的结果分析:

  • type: ref (使用了非唯一索引)

  • key: name (实际使用了 name 索引)

  • rows: 1 (预估扫描1行)

  • Extra: (空,或者 Using index 如果查询的列都被索引覆盖)
    结论: 这是一个高效的查询。

查询2:连接查询

sql

复制代码
EXPLAIN SELECT u.name, o.amount 
FROM users u 
JOIN orders o ON u.id = o.user_id 
WHERE u.name = 'Bob';

可能的结果分析(对于 orders 表这一行):

  • type: ref (使用 user_id 索引来关联)

  • key: user_id

  • rows: 5 (预估每个用户平均有5个订单)

  • Extra: (空)
    结论: 连接效率很高,因为双方都使用了索引。

查询3:性能不佳的查询

sql

复制代码
EXPLAIN SELECT * FROM users WHERE email = 'alice@example.com' ORDER BY name;

可能的结果分析:

  • type: ALL (全表扫描,因为 email 字段没有索引)

  • key: NULL

  • rows: 10000 (表中有1万行数据)

  • Extra: Using filesort (在内存或磁盘上进行排序)
    结论: 这是一个灾难性的查询。它进行了全表扫描,并且还有一个昂贵的文件排序。
    优化建议:

  1. email 字段添加索引。

  2. 如果经常需要按 name 排序,可以考虑建立 (email, name) 的复合索引,这样可以利用索引来查找和排序。


五、进阶工具:EXPLAIN ANALYZE (MySQL 8.0.18+)

EXPLAIN ANALYZE实际执行查询,并提供一个更详细的、包含实际执行时间的分析报告。

sql

复制代码
EXPLAIN ANALYZE SELECT * FROM users WHERE name = 'John';

输出格式类似于:

text

复制代码
-> Index lookup on users using idx_name (name='John')  (cost=0.35 rows=1) (actual time=0.025..0.027 rows=1 loops=1)

它提供了:

  • 实际执行时间 (actual time)。

  • 实际返回行数 (rows)。

  • 执行循环次数 (loops)。

  • 预估成本 (cost)。

这比传统的 EXPLAIN 提供了更精确的性能视图。

总结

检查点 目标
type 至少达到 range 级别,最好能到 ref。避免 ALL
key 确保查询实际使用了合适的索引,不为 NULL
rows 预估扫描行数尽可能小。
Extra 追求出现 Using index。警惕 Using temporaryUsing filesort

熟练掌握 EXPLAIN 是每个后端开发者和 DBA 的必备技能,它能帮助你从"猜测"优化变为"数据驱动"的优化。

相关推荐
Java 码农2 小时前
MySQL基础操作案例设计
数据库·mysql
csdn_aspnet3 小时前
如何在 Mac、Ubuntu、CentOS、Windows 上安装 MySQL 客户端
linux·windows·mysql·macos·centos
qq_5470261793 小时前
Canal实时同步MySQL数据到Elasticsearch
数据库·mysql·elasticsearch
csdn_aspnet4 小时前
CentOS 7 上安装 MySQL 8.0
linux·mysql·centos
stevenzqzq6 小时前
Android Hilt 入门教程_传统写法和Hilt写法的比较
android
一只小bit6 小时前
MySQL事务:如何保证ACID?MVCC到底如何工作?
数据库·mysql·oracle
wuwu_q6 小时前
用通俗易懂方式,详细讲讲 Kotlin Flow 中的 map 操作符
android·开发语言·kotlin
·云扬·7 小时前
使用pt-archiver实现MySQL数据归档与清理的完整实践
数据库·mysql
Hello.Reader7 小时前
Flink CDC 从 Definition 到可落地 YAML
大数据·adb·flink