深入解析MySQL EXPLAIN

在MySQL性能优化中,EXPLAIN命令是每个开发者都必须掌握的。它就像一台SQL查询的X光机,让我们能透视查询的内部执行机制,找出性能瓶颈的根源。

目录

  • 为什么要使用EXPLAIN?

  • EXPLAIN基础使用

  • 深入解析EXPLAIN输出字段

    • [1. id:查询标识符](#1. id:查询标识符)

    • [2. select_type:查询类型](#2. select_type:查询类型)

    • [3. table:访问的表](#3. table:访问的表)

    • [4. partitions:匹配的分区](#4. partitions:匹配的分区)

    • [5. type:连接类型(关键指标)](#5. type:连接类型(关键指标))

    • [6. possible_keys:可能使用的索引](#6. possible_keys:可能使用的索引)

    • [7. key:实际使用的索引](#7. key:实际使用的索引)

    • [8. key_len:索引使用的字节数](#8. key_len:索引使用的字节数)

    • [9. ref:索引的哪一列被使用](#9. ref:索引的哪一列被使用)

    • [10. rows:预估需要扫描的行数](#10. rows:预估需要扫描的行数)

    • [11. filtered:过滤百分比](#11. filtered:过滤百分比)

    • [12. Extra:额外信息(重要)](#12. Extra:额外信息(重要))

  • 实战分析:一个完整示例

  • 优化建议

  • 高级技巧

  • 常见问题排查

  • 总结

为什么要使用EXPLAIN?

在数据库性能优化中,盲目调优往往是事倍功半。EXPLAIN命令能够展示MySQL如何执行一条SELECT语句,包括:

  • 查询使用了哪些索引

  • 表的读取顺序如何

  • 数据是如何被检索和连接的

  • 需要扫描多少行数据

掌握这些信息,我们才能有针对性地进行索引优化、查询重构和配置调整。

EXPLAIN基础使用

sql 复制代码
-- 最基本的用法
EXPLAIN SELECT * FROM users WHERE age > 25;
​
-- 也可以写作
EXPLAIN FORMAT=TRADITIONAL SELECT * FROM users WHERE age > 25;
​
-- JSON格式输出,包含更详细信息
EXPLAIN FORMAT=JSON SELECT * FROM users WHERE age > 25;

深入解析EXPLAIN输出字段

1. id:查询标识符

  • 作用:表示SELECT查询的序列号

  • 解读规则

    • id相同:执行顺序从上到下

    • id不同:数值越大优先级越高,越先执行

    • id为NULL:表示是结果集,常见于UNION查询

sql 复制代码
EXPLAIN 
SELECT u.name, o.order_no 
FROM users u 
JOIN orders o ON u.id = o.user_id
WHERE u.status = 'active';

2. select_type:查询类型

  • SIMPLE:简单查询,不使用UNION或子查询

  • PRIMARY:查询中最外层的SELECT

  • SUBQUERY:子查询中的第一个SELECT

  • DERIVED:派生表(FROM子句中的子查询)

  • UNION:UNION中的第二个及后续查询

  • UNION RESULT:UNION的结果集

3. table:访问的表

  • 显示查询正在访问哪张表

  • 如果是派生表,会显示<derivedN>,其中N是id值

  • 如果是UNION结果,会显示<unionM,N>

4. partitions:匹配的分区

  • 对于分区表,显示查询将访问哪些分区

  • 非分区表则显示NULL

5. type:连接类型(关键指标)

这是判断查询效率的重要指标,性能从好到坏依次为:

类型 描述 性能等级
system 表只有一行记录(系统表) ★★★★★
const 通过索引一次就找到,用于primary key或unique key的等值查询 ★★★★★
eq_ref 唯一索引扫描,通常出现在多表join中 ★★★★☆
ref 非唯一索引扫描,返回匹配某个值的所有行 ★★★☆☆
range 索引范围扫描,常见于BETWEEN、>、<等操作 ★★☆☆☆
index 全索引扫描,遍历整个索引树 ★☆☆☆☆
ALL 全表扫描,性能最差,需要优化 ☆☆☆☆☆
sql 复制代码
-- const示例
EXPLAIN SELECT * FROM users WHERE id = 1;
​
-- ref示例  
EXPLAIN SELECT * FROM users WHERE status = 'active';
​
-- range示例
EXPLAIN SELECT * FROM users WHERE age BETWEEN 20 AND 30;
​
-- index示例
EXPLAIN SELECT id FROM users;  -- id是主键

6. possible_keys:可能使用的索引

  • 显示查询可能用到的所有索引

  • 如果为NULL,表示没有可能的索引

7. key:实际使用的索引

  • 查询实际使用的索引

  • 如果为NULL,表示没有使用索引

  • 可能与possible_keys中的索引不同,MySQL会选择它认为最优的索引

8. key_len:索引使用的字节数

  • 显示索引中使用的字节数

  • 可以判断是否使用了索引的全部部分

  • 计算规则:

    • 字符串类型:字符集字节数 × 字段长度 + 是否NULL(1) + 变长字段长度(2)

    • 数值类型:类型固定字节数

    • 时间类型:类型固定字节数

sql 复制代码
-- 假设name字段是varchar(100) utf8mb4
EXPLAIN SELECT * FROM users WHERE name = '张三';
-- key_len可能是 4*100+1+2 = 403

9. ref:索引的哪一列被使用

  • 显示索引的哪一列被使用了

  • 格式:数据库名.表名.列名 或 const

sql 复制代码
EXPLAIN SELECT * FROM users u 
JOIN orders o ON u.id = o.user_id;
-- 在orders表的执行计划中,ref可能是test.users.id

10. rows:预估需要扫描的行数

  • MySQL预估需要读取的行数

  • 数值越小越好

  • 注意:这是预估行数,不是实际行数

11. filtered:过滤百分比

  • 表示存储引擎返回的数据在服务器层过滤后,剩余多少百分比

  • 取值范围0-100,100表示没有过滤

  • 在MySQL 5.7及以上版本可用

12. Extra:额外信息(重要)

包含MySQL解决查询的额外信息:

含义 是否需要优化
Using index 使用了覆盖索引 ✓ 良好
Using where 在存储引擎检索行后进行了过滤 △ 视情况
Using temporary 使用了临时表,常见于GROUP BY、ORDER BY ✗ 需要优化
Using filesort 使用了文件排序 ✗ 需要优化
Using join buffer 使用了连接缓存 △ 视情况
Impossible WHERE WHERE条件永远为false ✓ 良好
Select tables optimized away 使用聚合函数访问索引 ✓ 良好
sql 复制代码
-- Using index示例(覆盖索引)
EXPLAIN SELECT id, name FROM users WHERE name LIKE '张%';
-- 如果name有索引,且查询字段都在索引中

实战分析:一个完整示例

sql 复制代码
-- 创建测试表
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100),
    age INT,
    status VARCHAR(20),
    created_at DATETIME,
    INDEX idx_age_status (age, status),
    INDEX idx_name (name)
);
​
-- 分析复杂查询
EXPLAIN 
SELECT u.name, COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.age > 25 
  AND u.status = 'active'
  AND u.name LIKE '张%'
GROUP BY u.id
HAVING order_count > 0
ORDER BY u.created_at DESC
LIMIT 10;

执行计划解读

  1. id相同,按顺序执行users表到orders表的连接

  2. type可能是range(age > 25)或ref(使用索引)

  3. key可能是idx_age_status索引

  4. Extra可能有Using where、Using temporary、Using filesort等

优化建议

  1. 关注type字段:尽量避免ALL和index,争取达到const、eq_ref、ref级别

  2. 留意Extra字段

    • 出现Using filesort:考虑优化ORDER BY

    • 出现Using temporary:考虑优化GROUP BY或JOIN

    • 争取出现Using index:使用覆盖索引

  3. 合理使用索引

    • 确保WHERE、JOIN、ORDER BY、GROUP BY涉及的列有索引

    • 注意最左前缀原则

    • 避免索引失效的场景(如函数操作、类型转换)

  4. 分析rows字段

    • 如果rows值远大于实际返回行数,可能需要更新统计信息
    复制代码
    ANALYZE TABLE users;

高级技巧

1. 使用EXPLAIN ANALYZE(MySQL 8.0+)

sql 复制代码
EXPLAIN ANALYZE 
SELECT * FROM users WHERE age > 25;
-- 提供实际执行时间和预估的对比

2. 查看优化器跟踪

sql 复制代码
SET optimizer_trace="enabled=on";
SELECT * FROM users WHERE age > 25;
SELECT * FROM information_schema.optimizer_trace;
SET optimizer_trace="enabled=off";

3. 对比不同写法的执行计划

sql 复制代码
-- 写法1
EXPLAIN SELECT * FROM users WHERE age = 25 OR status = 'active';
​
-- 写法2
EXPLAIN SELECT * FROM users WHERE age = 25 
UNION 
SELECT * FROM users WHERE status = 'active';

常见问题排查

1. 为什么没走索引?

  • 检查数据类型是否一致

  • 检查是否使用了函数或计算

  • 检查索引选择性(基数)

2. 如何查看索引基数?

sql 复制代码
SHOW INDEX FROM users;
-- 关注Cardinality列

3. 为什么rows预估不准?

sql 复制代码
-- 更新统计信息
ANALYZE TABLE users;
​
-- 强制使用索引(谨慎使用)
EXPLAIN SELECT * FROM users FORCE INDEX(idx_name) WHERE name LIKE '张%';

总结

EXPLAIN是MySQL性能优化不可或缺的工具。掌握每个字段的含义,就像拥有了一张SQL查询的详细地图。记住优化是一个迭代过程:

  1. 分析当前执行计划

  2. 识别瓶颈点(全表扫描、临时表、文件排序等)

  3. 实施优化措施(调整索引、重写查询等)

  4. 验证优化效果

  5. 重复此过程

通过持续监控和优化,你可以确保数据库查询始终保持在最佳性能状态。

最后:在真实生产环境中,一定要在测试环境验证EXPLAIN结果,并使用真实数据量进行测试,因为小数据量和大数据量的执行计划可能会有显著差异。

相关推荐
千里马学框架13 小时前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台13 小时前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone13 小时前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc14 小时前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo15 小时前
Kotlin 协程中的 Job 结构化并发与取消
android
sun00770016 小时前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼17 小时前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone18 小时前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen18 小时前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone18 小时前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui