MySQL优化查询速度,explain分析执行计划
前言
SQL查得慢,不知道为什么慢。用EXPLAIN看MySQL怎么执行这条SQL,就能找到问题在哪。
一、EXPLAIN怎么用
EXPLAIN SELECT * FROM users WHERE name = '张三';
输出很多列,重点看这几个:id、select_type、table、type、key、rows、Extra。
二、重点列详解
1. type(访问类型)
最重要的一列,看SQL查得快不快。
从好到差:
| type | 说明 | 速度 |
|---|---|---|
| system | 表只有一行 | 最快 |
| const | 主键或唯一索引查一次 | 很快 |
| eq_ref | 联表时主键关联 | 很快 |
| ref | 非唯一索引查 | 快 |
| range | 索引范围扫描 | 中 |
| index | 扫整个索引树 | 慢 |
| ALL | 全表扫描 | 最慢 |
目标: 至少达到range,最好是ref以上。
如果type是ALL,就是全表扫描,必须优化。
2. key(实际用到的索引)
key: NULL
说明没用索引。
key: idx_name
说明用到了idx_name索引。
3. rows(扫描行数)
MySQL预估要扫描多少行。
rows越大,越慢。
4. Extra(额外信息)
常见的:
- Using where:WHERE过滤
- Using index:覆盖索引,不用回表,好
- Using filesort:需要额外排序,不好
- Using temporary:用了临时表,不好
三、常见问题
问题1:type=ALL,全表扫描
原因: 没加索引,或者索引失效。
解决: 给WHERE条件的字段加索引。
问题2:key=NULL,没用索引
原因:
- 没建索引
- 索引失效了(函数、类型转换、前导模糊查询等)
解决: 建正确的索引,避免索引失效写法。
问题3:Using filesort
原因: ORDER BY的字段没走索引。
解决: 给ORDER BY的字段加索引。
问题4:Using temporary
原因: GROUP BY或DISTINCT用了临时表。
解决: 给GROUP BY的字段加索引。
四、实战案例
慢SQL
SELECT * FROM orders WHERE user_id = 100 ORDER BY create_time DESC LIMIT 10;
EXPLAIN结果:
| id | select_type | table | type | key | rows | Extra |
|---|---|---|---|---|---|---|
| 1 | SIMPLE | orders | ALL | NULL | 100000 | Using where; Using filesort |
问题:
- type=ALL,全表扫描
- key=NULL,没用索引
- rows=100000,扫了10万行
- Using filesort,还要额外排序
优化
加联合索引:
ALTER TABLE orders ADD INDEX idx_userid_createtime (user_id, create_time);
再EXPLAIN:
| id | select_type | table | type | key | rows | Extra |
|---|---|---|---|---|---|---|
| 1 | SIMPLE | orders | ref | idx_userid_createtime | 10 | Using where |
优化效果:
- type变成ref
- 用到了索引
- rows从10万变成10
- 没有filesort了
五、常用优化技巧
1. 避免SELECT *
只查需要的列,能用上覆盖索引。
2. WHERE条件加索引
经常查询的字段加索引。
3. 联合索引最左前缀
联合索引 (a,b,c),查询条件要从最左列开始。
4. 避免索引失效
- 不要对索引字段用函数
- 不要隐式类型转换
- 不要前导模糊查询
like '%xxx'
总结
EXPLAIN看这三点:
- type:是不是全表扫描
- key:有没有用到索引
- rows:扫描了多少行
记住: 慢SQL先EXPLAIN,找到问题再优化。
遇到问题加QQ23979811 协助处理
MySQL explain执行计划详解,分析SQL性能必备
前言
做后端开发的同学肯定都遇到过这种情况:写了一条SQL,查询特别慢,但是不知道为什么慢。是没走索引?还是全表扫描?还是数据量太大?
这时候就需要用MySQL的explain命令了。explain可以告诉你MySQL是怎么执行这条SQL的,走了哪个索引,扫了多少行,是不是全表扫描。搞懂了explain,你就能快速定位SQL慢的原因,然后针对性优化。
很多新手同学不知道explain怎么用,也不知道每一列是什么意思。这篇文章就把explain的知识点从头到尾讲清楚,看完之后你就能看懂执行计划了。
一、explain是什么
explain就是MySQL提供的一个分析工具。你在SELECT语句前面加上EXPLAIN,MySQL就不会真的执行这条SQL,而是告诉你它准备怎么执行这条SQL。
explain能告诉你什么
- 这条SQL有没有用到索引
- 扫了多少行数据
- 有没有全表扫描
- 用的是哪个索引
- 表连接的顺序是什么
二、怎么用explain
用法很简单,就在你的SELECT语句前面加上EXPLAIN就行:
EXPLAIN SELECT * FROM users WHERE name = '张三';
执行完之后,MySQL会返回一行结果,每一列都代表不同的信息。
三、explain的各列详解
explain返回的结果有很多列,我们重点讲几个常用的。
1. id
这一列表示SELECT的序号。如果有多个SELECT(比如子查询),id越大的越先执行。
2. select_type
这一列表示查询的类型,常见的有:
- SIMPLE:简单查询,没有子查询或者UNION
- PRIMARY:最外层的查询
- SUBQUERY:子查询里的第一个SELECT
- DERIVED:派生表(FROM子句里的子查询)
- UNION:UNION里的第二个及以后的SELECT
3. table
这一列表示当前这一行是在查哪张表。
4. type
这一列非常重要,表示MySQL是怎么找到数据的,也就是访问类型。从好到差依次是:
- system:表里只有一行数据,最好的情况
- const:通过索引一次就找到了,比如WHERE id=1,id是主键
- eq_ref:联表查询的时候,用主键或者唯一索引关联,最多匹配一行
- ref:用普通索引关联,可能匹配多行
- range:索引范围扫描,比如WHERE id BETWEEN 1 AND 10
- index:扫描整个索引树,比全表扫描好一点
- ALL:全表扫描,最差的情况,一定要优化
重点关注: 如果type是ALL或者index,说明这条SQL性能很差,要赶紧优化。
5. possible_keys
这一列表示理论上可能用到的索引。如果是NULL,说明没有用到索引。
6. key
这一列表示实际用到的索引。如果是NULL,说明真的没用到索引。
重点关注: 如果possible_keys有值,但是key是NULL,说明MySQL没有选择用索引,可能是数据量太小,或者索引失效了。
7. key_len
这一列表示用了索引的长度。可以用来判断用了联合索引的前几个字段。
8. ref
这一列表示用了哪个常量或者列来和索引做比较。
9. rows
这一列表示MySQL估计要扫多少行才能找到需要的数据。这个值越大,说明要扫的数据越多,性能越差。
10. Extra
这一列非常重要,包含了很多额外的信息,常见的有:
- Using where:服务器在存储引擎返回数据之后,又做了一层WHERE过滤
- Using index:用了覆盖索引,不需要回表,性能很好
- Using temporary:用了临时表,一般是GROUP BY或者ORDER BY的时候没有用到索引
- Using filesort:用了文件排序,ORDER BY没有用到索引,性能差
- Using join buffer:联表的时候用了缓冲,说明联表的字段没有索引
- Impossible WHERE:WHERE条件永远是false,根本查不到数据
重点关注: 如果Extra里出现了Using temporary或者Using filesort,说明SQL性能有问题,要优化。
四、重点看哪几列
很多同学看explain结果的时候,这么多列不知道重点看什么。其实重点看这几个:
- type:有没有全表扫描(ALL)
- key:有没有用到索引
- rows:扫了多少行
- Extra:有没有Using filesort或者Using temporary
只要这几个都没问题,这条SQL的性能一般就不会太差。
五、举个例子
比如我们执行这条SQL:
EXPLAIN SELECT * FROM orders WHERE user_id = 100 AND status = 1;
执行完之后看到:
+----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+
| 1 | SIMPLE | orders | ref | user_id | user_id | 4 | const | 100 | Using where |
+----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+
我们来分析一下:
- type是ref:说明用了普通索引,还不错
- possible_keys是user_id:理论上可以用user_id索引
- key是user_id:实际用了user_id索引
- rows是100:估计要扫100行
- Extra是Using where:说明返回之后又做了一层过滤
从这个结果来看,这条SQL用了索引,性能还可以。但是status字段没有用到索引,如果status过滤性好的话,可以考虑建一个(user_id, status)的联合索引。
六、explain的扩展用法
除了基本的explain,还有几个扩展的用法:
EXPLAIN PARTITIONS
看这条SQL会不会命中分区表:
EXPLAIN PARTITIONS SELECT * FROM orders WHERE ...;
EXPLAIN FORMAT=JSON
输出JSON格式的执行计划,信息更详细:
EXPLAIN FORMAT=JSON SELECT * FROM users WHERE ...;
常见坑
坑1:看到type是const就以为没问题
很多同学看到type是const就以为这条SQL性能很好。但是如果rows很大,或者Extra里有Using filesort,性能还是可能有问题。不能只看type,要综合看。
坑2:以为possible_keys有值就一定会用索引
很多同学看到possible_keys有值,就以为这条SQL一定会用到索引。其实不是,possible_keys只是说理论上可能用到,实际用不用要看key列。如果key是NULL,说明MySQL最后没有用这个索引。
坑3:rows是精确值
很多同学以为rows列是精确的行数,其实不是。rows是MySQL估计出来的行数,不是精确值。但是大致能反映出扫描的数据量大小。
坑4:以为Using filesort就是用了文件排序
很多同学看到Extra里有Using filesort就以为是真的用磁盘文件排序。其实不是,Using filesort只是说明MySQL做了额外的排序操作,不一定是用磁盘,数据量小的时候也可能在内存里排。
坑5:只看单表的explain,不看联表的
很多同学优化SQL的时候,只看单表的执行计划,不看联表的。其实联表的时候,表的连接顺序对性能影响很大。explain会按表的连接顺序一行一行列出来,要按顺序看。
总结
MySQL explain执行计划的核心知识点:
- explain的作用:看MySQL是怎么执行SQL的,不用真的执行
- type列最重要:从好到差是system > const > eq_ref > ref > range > index > ALL,出现ALL就是全表扫描
- key列:实际用到的索引,NULL就是没用到索引
- rows列:估计要扫多少行,越大越慢
- Extra列:重点关注Using filesort和Using temporary,出现这两个就要优化
- 重点看type、key、rows、Extra这四列,其他的了解就行
- rows是估计值,不是精确的,但是能反映大致的数据量
explain是分析SQL性能的必备工具,不管是面试还是实际开发,都是必须掌握的。以后遇到SQL慢,先explain一下,看看是哪里出了问题。
遇到问题加QQ23979811 协助处理