explain 让你的 sql 写的更踏实
source link: https://www.tuicool.com/articles/6zi2MjV
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
本文主要通过一些实例介绍如何使用 mysql 中的 explain 关键字分析查询语句,好让我们的查询语句写的更踏实,也让我们养成用 explain 分析的习惯,养成查询语句的写法习惯。
概念介绍
在 mysql 里, explain
是执行计划的意思,即可以通过该命令看出 mysql 是如何执行该条 sql 的,可以通过分析索引,表结构等方面来优化你的慢查询语句。
mysql 使用 explain + sql 语句
来查看执行计划,执行结果有十个字段,具体描述如下:
id
id相同,执行顺序由上至下;id不同,id的序号会递增,id值越大优先级越高,越先被执行
select_type
主要是用于区别普通查询、联合查询、子查询等的复杂查询
table
当前执行的表
type
访问类型
possible_keys
可能使用的索引
key
实际使用的索引
key_len
使用的索引的长度
ref
显示索引的哪一列被使用了
rows
查询过程中可能扫描的行数
Extra
解析查询的额外信息,通常会显示是否使用了索引,是否需要排序,是否会用到临时表等
其中 type
字段可选值如下,性能从低到高排列:
ALL
全数据表扫描
index
全索引表扫描
RANGE
对索引列进行范围查找
INDEX_MERGE
合并索引,使用多个单列索引搜索
REF
根据索引查找一个或多个值
EQ_REF
搜索时使用primary key 或 unique类型
CONST
常量,表最多有一个匹配行,因为仅有一行,在这行的列值可被优化器剩余部分认为是常数,const表很快,因为它们只读取一次
SYSTEM
系统,表仅有一行(=系统表)。这是const联接类型的一个特例
OK,概念匆匆介绍之后,结合自己的分析习惯,下面会通过实例聚焦 type
、 key
、 rows
、 Extra
这几个字段,来介绍如何分析我们的查询语句。
实例分析
数据初始化
新建测试表,插入 10 w 数据:
CREATE TABLE `test` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `a` int(11) NOT NULL, `b` int(11) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB; -- 批量插入 10w 数据 -- DROP PROCEDURE IF EXISTS batchInsert DELIMITER $ CREATE PROCEDURE batchInsert () BEGIN DECLARE i INT DEFAULT 1; START TRANSACTION; WHILE i<=100000 DO INSERT INTO test (a,b) VALUES (i,i); SET i=i+1; END WHILE; COMMIT; END $ CALL batchInsert ();
全表查询
目前默认只有一个主键索引,我们分析下全表查询:
mysql> explain select * from test;id select_type table type possible_keys key key_len ref rows Extra
1
SIMPLE
test
ALL
NULL
NULL
NULL
NULL
100333
NULL
其中 type 值为 ALL,表示全表扫描了,我们看到 rows 这个字段显示有 100332 条,实际上我们一共才 10w 条数据,说明这个字段只是 mysql 的一个预估,不总是准确的。这个 test 表一次真实的查询时间为:2.708000s,可见这种全表扫描的效率非常低,是需要被优化的。
索引查询
接下来我们分别给字段 a 和 b 添加普通索引。
mysql> alter table test add index idx_a(a); mysql> alter table test add index idx_b(b);
看下下面这条 sql:
mysql> explain select * from test where a > 10000;id select_type table type possible_keys key key_len ref rows Extra
1
SIMPLE
test
ALL
idx_a
NULL
NULL
NULL
100333
Using where
我们发现 type
竟然不是 index, 刚刚不是给字段 a 添加索引了么?还有 possible_keys
也显示了有 a_index,但是 key
显示 null,表示实际上不会使用任何索引,这是为什么呢?
这是因为 select * 的话还需要回到主键索引上查找 b 字段,这个过程叫 回表
。
这条语句会从索引中查出 9w 条数据,也就是说这 9w 条数据都需要 回表
操作,全表扫描都才 10w 条数据,所以在 mysql 最后的决策是还不如直接全表扫描得了,至少还免去了回表过程了。
当然,最后决策是否用索引不是固定的,mysql 会比较各种查询的代价,我们把上面的 sql 中 where 条件再稍微改造一下。
mysql> explain select * from test where a > 90000;id select_type table type possible_keys key key_len ref rows Extra
1
SIMPLE
test
range
idx_a
idx_a
4
NULL
9999
Using index condition
再看这次 type
为 range 了, key
为 a_index,表示使用了 a 索引,如我们所愿了。这是因为满足这次索引中查出只有 10000 条数据,mysql 认为 10000 条数据就算回表也要比全表扫描的代价低,因而决定查索引。
上面两条查询说明 mysql 会比较 索引 + 回表
和 直接全表扫描
的查询性能,选择其中更好的作为最后的查询方式,这就是 mysql 优化器的作用了。
还有一点就是这次 Extra
字段中值为 Using index condition,这是指条件过滤的时候用到了索引,但因为是 select * ,所以还是需要回表,再看下面这个语句。
mysql> explain select a from test where a > 90000;id select_type table type possible_keys key key_len ref rows Extra
1
SIMPLE
test
range
idx_a
idx_a
4
NULL
9999
Using where; Using index
注意这次 Extra
的值为 Using where; Using index,表示查询用到了索引,且要查询的字段在索引中就能拿到,所以不需要回表,显然这种效率比上面的要高,这也是日常开发中不建议写 select * 的原因,尽量只查询业务所需的字段。
排序查询
再来看一个带排序的查询。
mysql> explain select a from test where a > 90000 order by b;id select_type table type possible_keys key key_len ref rows Extra
1
SIMPLE
test
range
idx_a
idx_a
4
NULL
9999
Using index condition; Using filesort
我们知道索引本来就是有序带,但这个 Extra
中返回了一个 Using filesort,说明无法利用索引完成排序,需要从内存或磁盘进行排序,具体哪种排序 explain 是没有体现的。
总之,这种情况也是需要优化的,尽量能利用索引的有序性,比如下面:
mysql> explain select a from test where a > 90000 order by a;
id select_type table type possible_keys key key_len ref rows Extra1
SIMPLE
test
range
idx_a
idx_a
4
NULL
9999
Using where; Using index
这次 Extra
值有 Using index 了,表示使用上了索引。
复合索引
我们再创建一个复合索引看看。
mysql> alter table test add index idx_a_b(a,b);
看下之前的查询 mysql> explain select * from test where a > 10000;
id select_type table type possible_keys key key_len ref rows Extra1
SIMPLE
t
range
idx a,idx a_b
idx a b
4
NULL
50166
Using where; Using index
这条 sql 刚刚在没有创建复合索引的时候,是走的全表扫描,现在看 Extra
有 Using index,说明利用了覆盖索引,同样也免去了回表过程,即在 idx a
b 索引上就能找出要查询的字段。
总结
本文通过几个实例介绍了如何使用 explain
来分析一条 sql 的查询计划,例子都很简单,旨在能通俗易懂的说明白一些常见的查询问题,也让我们能养成良好的查询习惯。
参考
https://dev.mysql.com/doc/refman/5.7/en/explain-output.html#explain_rows https://blog.csdn.net/poxiaonie/article/details/77757471 https://www.cnblogs.com/tufujie/p/9413852.html https://www.xttblog.com/?p=4225
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK