一、什么是 EXPLAIN?

EXPLAIN 是 MySQL 提供的一个用于分析 SQL 查询执行计划(Execution Plan)的命令。它不会真正执行 SQL,而是告诉你 MySQL 打算如何执行这条语句。

语法:

EXPLAIN SELECT ...;
-- 或更详细的格式(MySQL 5.6+)
EXPLAIN FORMAT=JSON SELECT ...;

二、EXPLAIN 输出字段详解(以传统表格格式为例)

列名 含义 关键原理说明
id 查询中 SELECT 的序列号 id 相同表示同一层级;id 越大优先级越高(子查询);NULL 表示结果集合并(如 UNION)
select_type SELECT 类型 如 SIMPLE(简单查询)、PRIMARY(最外层)、SUBQUERY(子查询)、DERIVED(派生表)、UNION 等,反映查询结构复杂度
table 涉及的表名 可能是实际表、别名、或 <derivedN>(派生表)
partitions 匹配的分区(若使用分区表) 显示查询涉及哪些分区,体现分区裁剪(Partition Pruning)能力
type 访问类型(最重要之一) 表示 MySQL 如何查找行:
• system/const:主键或唯一索引等值查询
• eq_ref:主键/唯一索引连接
• ref:非唯一索引等值
• range:范围扫描
• index:全索引扫描
• ALL:全表扫描(性能最差)
possible_keys 可能用到的索引 优化器考虑使用的索引候选
key 实际使用的索引 若为 NULL,说明未走索引
key_len 使用索引的长度(字节) 可判断复合索引用了前几列(如 INT=4字节,VARCHAR(255) utf8mb4 最多1020字节+2)
ref 与索引比较的列或常量 如 const、func、或某表的字段
rows 估算扫描行数 越小越好,反映选择性
filtered 按表条件过滤后剩余百分比 rows × filtered% ≈ 实际参与后续操作的行数
Extra 额外信息(极其重要) 常见值:
• Using index:覆盖索引(无需回表)
• Using where:服务层过滤
• Using temporary:用临时表(如 GROUP BY 无索引)
• Using filesort:无法用索引排序,需额外排序
• Using index condition:索引条件下推(ICP)

三、从 EXPLAIN 看 MySQL 查询执行原理

1. 查询解析与优化

  • MySQL 解析 SQL → 生成 AST(抽象语法树)
  • 优化器基于统计信息(如索引基数、表大小)选择最优执行路径
  • EXPLAIN 展示的就是优化器决策结果

2. 索引选择机制

  • 优化器评估 possible_keys 中每个索引的成本(I/O + CPU)
  • 成本最低者被选为 key
  • key 为 NULL,可能因为:
    • 无合适索引
    • 全表扫描比走索引更快(如返回大量数据)

3. 连接顺序(Join Order)

  • 多表 JOIN 时,MySQL 决定驱动表(先查哪张表)
  • EXPLAIN 中靠前的表通常是驱动表
  • 驱动表应尽量小(减少嵌套循环次数)

4. 执行方式

  • Nested-Loop Join:MySQL 主要连接算法
  • 若出现 Using temporary; Using filesort,说明需要中间结果处理,性能较差

5. 覆盖索引 vs 回表

  • Using index 表示所需字段都在索引中,避免回表(InnoDB 聚簇索引特性)
  • 否则需通过主键回聚簇索引取数据(额外 I/O)

四、实战示例

EXPLAIN SELECT u.name, o.amount
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.status = 'active';

假设:

  • users(id PK, status, name)
  • orders(id PK, user_id, amount)
  • users 有索引 (status)
  • orders 有索引 (user_id)

理想 EXPLAIN 结果:

  • users 表:type=ref, key=status, Extra=Using index condition
  • orders 表:type=ref, key=user_id, Extra=Using where

说明:

  • 先用 status 索引快速定位活跃用户
  • 再用 user_id 索引关联订单
  • SELECT 字段都在索引中,则可能 Using index

五、高级技巧

  1. 使用 EXPLAIN ANALYZE(MySQL 8.0+)

    • 不仅显示计划,还显示实际执行时间、循环次数等
    EXPLAIN ANALYZE SELECT ...;
    
  2. 查看优化器 trace

    SET optimizer_trace="enabled=on";
    SELECT ...;
    SELECT * FROM information_schema.optimizer_trace;
    
    • 可看到为何没选某个索引、成本计算细节

六、总结

通过 EXPLAIN,我们可以洞察:

  • MySQL 是否使用了索引
  • 查询是否高效(避免全表扫描、临时表、文件排序)
  • 连接顺序是否合理
  • 是否存在回表、覆盖索引等优化机会

核心原则

让 MySQL 尽可能少读数据、少排序、少临时表、快定位。


Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐