
你的SQL真的在”努力工作”吗?
“这个查询昨天还好好的,今天怎么突然变慢了?”——这是DBA最常听到的灵魂拷问。当我们面对一个执行缓慢的SQL语句时,数据库就像个沉默的工人,只管埋头做事却从不解释工作流程。
直到Explain工具的出现,它如同给数据库装上了实时工作监控,让我们能清晰看到:索引是否被正确使用、表连接顺序是否合理、数据扫描量是否超标。今天将通过真实场景案例,带你解锁这个优化利器的核心用法,让你的SQL查询效率提升300%!
一、Explain执行计划字段介绍
在MySQL中,使用EXPLAIN命令可以协助我们了解SQL查询的执行计划。下面是通过表格方式展示的关于EXPLAIN输出结果中可能出现的所有字段及其详细解释:
|
字段名 |
描述 |
|
id |
每个SELECT关键字的标识符。它指示了SELECT语句的执行顺序。较大的数字优先级更高。如果两个id值一样,则它们是同级操作。 |
|
select_type |
表明对应行是属于哪种类型的SELECT。常见的类型包括:SIMPLE(简单查询)、PRIMARY(最外层查询)、SUBQUERY(子查询内层第一个SELECT)等。 |
|
table |
当前操作涉及的表名称或别名。 |
|
partitions |
如果查询的是分区表,这里会列出当前查询涉及的分区名称。如果没有使用分区,则为NULL。 |
|
type |
显示连接类型,从最佳到最差排序如下:system > const > eq_ref > ref > range > index > ALL。 |
|
possible_keys |
指出MySQL可以选用的索引。这只是一个估算,并不代表实际使用的索引。 |
|
key |
实际上被MySQL选用来查找数据行的索引。如果为NULL,则没有使用索引。 |
|
key_len |
使用索引的长度。根据这个值可以判断是否使用了复合索引以及使用了复合索引中的哪些列。 |
|
ref |
显示哪一列或常量与key一起被用来从表中选择行。 |
|
rows |
估算需要检查的记录数。此数值越小越好。 |
|
filtered |
表明按表条件过滤的行百分比。值范围从0到100。 |
|
Extra |
包含不适合在其他列中显示但对执行计划超级重大的额外信息。例如,“Using where”、“Using temporary”、“Using filesort”等。 |
- Using where: 表明MySQL服务器将在存储引擎检索行后再进行过滤。
- Using temporary: 表明MySQL需要使用临时表来处理查询。一般发生在GROUP BY和ORDER BY操作中。
- Using filesort: MySQL需要额外的一次传递来找出如何按排序顺序检索行。
- Select tables optimized away: 在基于聚合函数和索引的情况下,MySQL优化器已经将表移除,由于可以直接从索引中获取所有必要的信息。
二、执行计划全景解析:从type到Extra的优化地图
2.1. type字段:查询的”工作方式”
-- 案例:用户订单统计查询
EXPLAIN SELECT * FROM orders WHERE user_id = 10086;
当type显示为ALL时,表明全表扫描:
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | orders | ALL | NULL | NULL | NULL | NULL | 8924 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
优化方案:为user_id添加索引
ALTER TABLE orders ADD INDEX idx_user(user_id);
优化后的执行计划变为ref类型:
+----+-------------+--------+------+---------------+-----------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+-----------+---------+-------+------+-------+
| 1 | SIMPLE | orders | ref | idx_user | idx_user | 5 | const | 23 | NULL |
+----+-------------+--------+------+---------------+-----------+---------+-------+------+-------+
2.2. Extra字段:隐藏的性能信号灯
- Using filesort:需要额外排序
-- 危险案例:无索引排序
EXPLAIN SELECT * FROM products ORDER BY price DESC;
+----+-------------+----------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+------+---------------+------+---------+------+------+----------------+
| 1 | SIMPLE | products | ALL | NULL | NULL | NULL | NULL | 1568 | Using filesort |
+----+-------------+----------+------+---------------+------+---------+------+------+----------------+
优化方案:建立联合索引
ALTER TABLE products ADD INDEX idx_price_name(price, product_name);
2.3. rows字段:数据扫描量的照妖镜
-- 大表关联查询优化前
EXPLAIN SELECT * FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.create_time > '2023-01-01';
+----+-------------+-------+--------+---------------+---------+---------+-----------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+-----------------+------+-------------+
| 1 | SIMPLE | u | range | PRIMARY | PRIMARY | 4 | NULL | 2345 | Using where |
| 1 | SIMPLE | o | ref | idx_user | idx_user| 5 | shop.u.id | 7 | NULL |
+----+-------------+-------+--------+---------------+---------+---------+-----------------+------+-------------+
优化策略:使用覆盖索引
ALTER TABLE users ADD INDEX idx_create_time_id(create_time, id);
三、经典优化场景实战
场景1:分页查询深度优化
-- 原始分页(性能瓶颈)
EXPLAIN SELECT * FROM logs ORDER BY id DESC LIMIT 100000, 20;
+----+-------------+-------+-------+---------------+---------+---------+------+--------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+------+--------+-------+
| 1 | SIMPLE | logs | index | NULL | PRIMARY | 4 | NULL | 100020 | NULL |
+----+-------------+-------+-------+---------------+---------+---------+------+--------+-------+
优化方案:游标分页法
SELECT * FROM logs
WHERE id < last_seen_id
ORDER BY id DESC
LIMIT 20;
场景2:联表查询索引失效
-- 类型不匹配导致索引失效
EXPLAIN SELECT * FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.mobile = 13800138000;
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
| 1 | SIMPLE | u | ref | idx_mobile | idx_mobile | 63 | const | 1 | Using where |
| 1 | SIMPLE | o | ALL | idx_user | NULL | NULL | NULL | 8924 | Using join |
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
问题诊断:mobile字段定义为varchar,但查询使用数字值
优化方案:保持类型一致
SELECT * FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.mobile = '13800138000';
四、总结
通过Explain执行计划,我们就像拥有了数据库的X光透视眼。记住这三个黄金法则:
- 永远追求更优的type(至少达到range级别)
- 消灭危险的Extra提示(如Using filesort)
- 严格控制扫描行数(rows越小越好)
















- 最新
- 最热
只看作者