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 的必备技能,它能帮助你从"猜测"优化变为"数据驱动"的优化。

相关推荐
码熔burning8 小时前
MySQL 8.0 新特性爆笑盘点:从青铜到王者的骚操作都在这儿了!(万字详解,建议收藏)
数据库·mysql
xiaolizi5674898 小时前
安卓远程安卓(通过frp与adb远程)完全免费
android·远程工作
教程分享大师8 小时前
创维E900V22D当贝固件全网通线刷机包 刷机教程开启adb权限及root权限
adb
吉凶以情迁8 小时前
tcl 电视进入开发者模式以及adb进入设置以及各电视打开开发者模式用电脑控制的办法
adb·电脑
阿杰100019 小时前
ADB(Android Debug Bridge)是 Android SDK 核心调试工具,通过电脑与 Android 设备(手机、平板、嵌入式设备等)建立通信,对设备进行控制、文件传输、命令等操作。
android·adb
猫头虎9 小时前
2025最新OpenEuler系统安装MySQL的详细教程
linux·服务器·数据库·sql·mysql·macos·openeuler
_Mistletoe9 小时前
adb基础操作及如何使用adb实现设备间的文件传输
adb
XuanRanDev9 小时前
Mumu模拟器12开启ADB调试方法
adb
梨落秋霜9 小时前
Python入门篇【文件处理】
android·java·python
遥不可及zzz11 小时前
Android 接入UMP
android