mysql菜鸟教程
13.6 EXPLAIN语句:查看查询执行计划
在数据库优化中,了解 MySQL 是如何执行你的 SQL 语句至关重要。EXPLAIN 语句就是 MySQL 提供的“透视镜”,它可以展示查询的执行计划,让你看到是否使用了索引、表的连接顺序、扫描行数等信息,从而帮助你诊断慢查询并优化索引。
一、什么是 EXPLAIN?
EXPLAIN 关键字用于查看一个 SQL 语句的执行计划。执行计划是 MySQL 优化器决定如何执行查询的步骤和路径。通过 EXPLAIN,你可以了解:
表的读取顺序
数据表之间的连接类型
哪些索引被使用
每张表可能扫描的行数
是否使用了临时表或文件排序等
这些信息是优化查询的重要依据。
基本语法
EXPLAIN SELECT ...;
例如:
EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';
你还可以在 EXPLAIN 后加上 FORMAT=JSON 获取更详细的 JSON 格式输出:
EXPLAIN FORMAT=JSON SELECT ...;
二、EXPLAIN 输出列详解
执行 EXPLAIN 后,MySQL 会返回一行或多行结果,每一行代表查询中的一个表(可能是物理表或派生表)。下面我们以一张典型的结果集为例,解释每一列的含义。
type 列详解
type 列表示 MySQL 如何查找表中的行,其值从上到下性能递减:
Extra 列常见值
三、实战示例
我们通过几个示例来学习如何使用 EXPLAIN 分析查询。
示例表结构
CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL, email VARCHAR(100) NOT NULL, age INT, created_at DATETIME, INDEX idx_email (email), INDEX idx_age (age) ); CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, amount DECIMAL(10,2), order_date DATE, INDEX idx_user_id (user_id), INDEX idx_order_date (order_date) );
1. 简单查询,使用索引
EXPLAIN SELECT * FROM users WHERE email = 'john@example.com';
可能输出:
解释:
type = ref:使用了非唯一索引等值查询。
key = idx_email:实际使用的索引。
rows = 1:估计只扫描一行。
Extra = Using index condition:使用了索引条件下推(ICP),但这里不是覆盖索引,因为 SELECT * 需要回表。
2. 无索引的列查询
EXPLAIN SELECT * FROM users WHERE username = 'john';
如果 username 没有索引:
解释:
type = ALL:全表扫描。
rows = 1000:估计扫描所有行。
建议为 username 添加索引。
3. 复合索引与最左前缀
假设我们为 orders 表创建一个复合索引:
CREATE INDEX idx_user_date ON orders(user_id, order_date);
查询:
EXPLAIN SELECT * FROM orders WHERE user_id = 1 AND order_date = '2024-01-01';
输出:
如果只查询 order_date:
EXPLAIN SELECT * FROM orders WHERE order_date = '2024-01-01';
可能 type = ALL 或 index,因为无法使用复合索引的左前缀,除非有单独的索引。
4. 排序与文件排序
EXPLAIN SELECT * FROM users ORDER BY age;
如果 age 有索引:
如果没有索引:
Using filesort 表示需要额外的排序操作,当数据量大时可能很慢,考虑为排序列创建索引。
5. 连接查询
EXPLAIN SELECT * FROM users u JOIN orders o ON u.id = o.user_id WHERE u.age > 30;
输出通常有两行,分别对应 users 和 orders。注意 type 和 Extra,观察是否使用了索引。
四、如何利用 EXPLAIN 优化查询?
检查 type 列:尽量让 type 达到 ref 或 range 级别,避免 ALL。
观察 key 列:确保使用了预期的索引。如果 possible_keys 有候选但 key 为 NULL,说明优化器认为全表扫描更快(可能因为数据量小或索引选择性差)。可以尝试用 FORCE INDEX 或调整查询。
关注 rows 列:估算扫描的行数,与表的总行数对比,如果差距太大,考虑优化索引。
分析 Extra 列:
出现 Using temporary 或 Using filesort 时,考虑添加合适的索引或改写查询。
出现 Using index 很好,说明使用了覆盖索引。
出现 Using where 正常,但如果加上 Using index 可能更好。
复合索引的顺序:根据查询中最常出现的列顺序调整索引列顺序,尽量利用左前缀。
关注 key_len:可以判断复合索引实际使用了多少列,如果比预期短,说明没有完全用上。
五、注意事项
EXPLAIN 只是估算,并不实际执行查询,因此 rows 和 filtered 是估计值,可能与实际执行有偏差。
对于复杂的查询,EXPLAIN 可能会产生多行,需要仔细阅读。
在 MySQL 5.6 及以后,可以使用 EXPLAIN FORMAT=JSON 获得更详细的信息,包括成本估算。
有些 Extra 信息如 Using index condition(ICP)是优化手段,不必过度担心。
对于 UPDATE 或 DELETE 语句,也可以使用 EXPLAIN,但需要先改成 SELECT 以分析执行计划(例如 EXPLAIN SELECT ... FROM 表 WHERE ...)。
六、实际优化案例
假设有一个慢查询:
SELECT * FROM orders WHERE order_date BETWEEN '2024-01-01' AND '2024-01-31' ORDER BY amount DESC LIMIT 10;
执行 EXPLAIN 发现 type = ALL 且 Extra = Using filesort。解决方案:
为 order_date 创建索引以加速范围查询。
如果仍然需要文件排序,考虑创建复合索引 (order_date, amount),使排序也能利用索引。
修改后再次执行 EXPLAIN,应看到 type = range 且 Extra = Using index condition 或 Using where; Using index(如果覆盖索引)。
七、小结
EXPLAIN 是分析查询执行计划的利器,能帮助你理解查询的性能瓶颈。
重点关注的列:type、key、rows、Extra。
优化目标:避免全表扫描,减少扫描行数,避免文件排序和临时表。
结合索引设计,持续使用 EXPLAIN 验证优化效果。

发表评论
所有评论