深入解析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结果,并使用真实数据量进行测试,因为小数据量和大数据量的执行计划可能会有显著差异。

相关推荐
_李小白2 小时前
【Android 美颜相机】第十一天:GPUImageFilter解析
android·数码相机
dawudayudaxue2 小时前
sqlite在安卓下使用ndk的交叉编译
android·数据库·sqlite
YIN_尹2 小时前
【MySQL】表的约束(下)
android·数据库·mysql
爱编码的傅同学2 小时前
【线程的同步与互斥】初识互斥量与锁
android·java·开发语言
_李小白2 小时前
【Android 美颜相机】第十天:YUV420SP和RGB
android·数码相机
2501_944526422 小时前
Flutter for OpenHarmony 万能游戏库App实战 - 收藏功能实现
android·java·开发语言·javascript·python·flutter·游戏
2501_944526422 小时前
Flutter for OpenHarmony 万能游戏库App实战 - 个人中心实现
android·java·javascript·python·flutter·游戏
Jomurphys2 小时前
Kotlin - 引用操作符 ::
android·kotlin
恋猫de小郭2 小时前
Meta ShapeR :基于随机拍摄视频的 3D 物体生成,未来的 XR 和机器人基建支持
android·flutter·3d·ai·音视频·xr