简书-架构专栏

MySQL干货之-利用EXPLAIN优化查询

2018-01-22  本文已影响0人  metmax

​ 在工作中,经常会碰到一些慢查询,Explain可以帮我们更详细的了解MySQL查询的执行计划,用法也很简单Explain 后面跟上SELECT语句即可。执行完之后,会显示一行有多个列的记录,可能很多人和我一样,对EXPLAIN里面字段的含义,并没有深入的去了解过,处于一知半解的状态,只知道一些最常见的。

​ 下面我根据MySQL官方文档,查阅了很多资料,再结合我自己的理解,对EXPLAIN的字段和值做了详细的描述,在总结过程中,也发现了自己的很多知识漏洞,很多时候,总是会想当然的认为,这个就是对的,并没有严密的逻辑验证,大脑喜欢偷懒,正所谓好记性不如烂笔头,写的过程也是对自己知识点掌握程度的批判和考验。

关于EXPLAIN

​ EXPLAIN返回一行记录,通过Explain可以获取到很多信息,如:不同表的查询顺序,查询用了哪些表,能使用哪些索引以及真正用到了哪些索引,用了哪种连接类型,是否有临时表和文件排序等。这些因素对查询的效率有直接的相关,想要使查询更高效,需要对这些条件做一个好的优化。

​ EXPLAIN有12个字段,每个字段对查询优化的权重比不一样,也就是说并不是所有字段都很重要。type,key,Extra字段相对其它字段来说,对查询效率的影响更大,优化查询的时候,先把注意力放到这些字段会比其它字段来得更加直接有效,下面开始具体内容。

EXPLAIN语法

user_info表为例:

explain select * from `user_info` where uid = 5

结果:

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE user_info NULL const PRIMARY PRIMARY 8 const 1 100.00 NULL

EXPLAIN字段说明

标注星号的字段为重点

id:

SELECT语句的标识符,代表SELECT查询在整个查询中的序号。这个值也可能为NULL,如果这一行是UNION的结果。

select_type:

SELECT查询的类型,该类型的值有11种类型。例如,示例中的值为SIMPLE,表示该查询是一个简单的查询(即:没有子查询和UNION)。

table:

大多数情况下表示输出行所引用的表名,它也可能是下列值之一:

partitions:

只对分区表有意义。意思是查询所匹配到的分区,如果该表为非分区表,则它的值为NULL

*type:

查询的join类型,注意单表查询也被当做join的特例,并不一定要两张表。连接类型详情下面会详细介绍。

possible_key:

possible_key列是指,在查询中能够被MySQL用到的索引,但在实际情况中,不一定会被全部用到,这取决于MySQL优化器的选择,假设possible_keyA,B,C,3个索引,优化器经过分析认为A索引不需要用,那么实际执行的时候只会用到B,C索引。实际应用中,该列经常帮我们对SQL查询进行优化,如果它的值为NULL,说明没有能被用到的索引,这种情况下,需要调整SQL语句和优化表的索引。

*key:

查询中实际用到的索引,要注意,该列的值可能包含possible_key列中没有出现的索引,当查询满足覆盖索引的条件时,possible_keys列为NULL,索引仅在key列显示,MySQL只需要扫描索引树,不用到实际的数据行检索即可得到结果,查询会更高效,Extra列显示USING INDEX,则证明使用了覆盖索引。 也可以通过FORCE INDEX,USE INDEXIGNORE INDEX来强制使用或忽略possible_key列中的索引。

覆盖索引概念

如果索引包含所有满足查询需要的数据的索引成为覆盖索引(Covering Index)。

假设有一个user表,假设索引A包含了col1,col2,col3三个字段,criteria为标准条件。

Query 1:
select * from user where criteria

Query 1使用了索引查询,获取到数据行的主键,但是仍然需要根据主键值扫描实际的数据行。

Query 2:
select `col1`,`col2` where criteria

Query 2中,索引A已经包含了它需的字段,也就是说Query 2不用再去实际的数据行获取数据了,只要扫描完索引树就行了,这样就省了一个步骤,索引树往往比实际的数据表小,所以效率很高,这就是覆盖索引

key_len:

实际用到的索引字段长度,越短越好。

ref:

ref列显示哪个列或者常数和索引比较筛选出结果。

rows:

rows列表示MySQL认为执行查询必须检查的行数,对Innodb表来说,这是一个预估值,可能并不是确切的值。

filtered:

filtered的意思是,首先MySQL利用索引,例如,用range范围扫描出符合的行,如果扫描符合条件的估计值是100行,rows显示估计的值就是100,这一步是存储引擎根据索引筛选后的值,然后在Server层根据其余的WHERE条件过滤。

​ 被过滤器过之后,符合条件的还剩下20行,也就是剩下20%,20%就是filtered中的值。很显然,直接在存储引擎层筛选出20行比先筛选出100行再过滤要更好,通常情况下,filtered的值越大可能意味着索引越好。

​ 另一方面看,你也可以完全忽略filtered,因为这个值在大多数情况下只是一个不准确的估计,应该把注意力放到优化其它更有用的字段上,尤其是type,key,Extra。例如:尽量避免filesort排序,使用索引排序。或者有一个更好的type值,对性能的提升是非常巨大的,这种情况,即使filtered的值低也没关系。假设一个查询Atype=all,filtered=0.1%。那么首要先关注type字段,可通过添加索引来优化,可以先不管filtered

​ 所以对这个值不需要太认真,即使100%也不意味着索引一定好,反过来也不一定说明索引差,type比它更能说明索引的好坏。

*Extra:

这个列包含Mysql解决查询的详细信息,详情见下方。

EXPLAIN字段值说明:

select_type:

select_type 值 描述
SIMPLE 简单的SELECT查询(没有UNION和子查询)
PRIMARY 一个需要union操作或者含有子查询的select,位于最外层的单位查询的select_type即为primary。且只有一个
UNION UNION连接的select查询,除了第一个表外,第二个及以后的表select_type都是union
DEPENDENT UNION union一样,出现在unionunion all语句中,但是这个查询要受到外部查询的影响
UNION RESULT UNION之后的结果集
SUBQUERY 除了from字句中包含的子查询外,其他地方出现的子查询都可能是subquery
DEPENDENT SUBQUERY 与dependent union类似,表示这个subquery的查询要受到外部表查询的影响
DERIVED FROM字句中出现的子查询。语法:SELECT ... FROM (subquery) [AS] tbl_name ...
MATERIALIZED 被物化的子查询
UNCACHEABLE SUBQUERY 对于外层的主表,子查询不可被物化,每次都需要计算(耗时操作)
UNCACHEABLE UNION UNION操作中,内层的不可被物化的子查询(类似于UNCACHEABLE SUBQUERY)

通过 **物化 ** 优化子查询的原理:

​ 优化器使用物化的方式能让子查询更高效的执行,类似缓存技术,把第一次查询的结果存起来,避免多次的耗时操作,同时也有它自身的限制,不是所有子查询都能被物化的。物化技术把子查询产生的结果放在一个临时表中,如果数据量小的话,通常是在内存中完成,数据大的时候就降级到磁盘进行,速度也会慢很多。首先,MySQL得到子查询的结果,然后把结果放到临时表中,在随后的任何时间,当需要这个结果时,MySQ就再次引用这个临时表,不需要再执行计算了。优化器可能会使用哈希索引(复杂度为O(1),很快)来快速且低成本的查找表,这个索引是唯一的,避免了重复,能使表更小。

SELECT * FROM t1
WHERE t1.a IN (SELECT t2.b FROM t2 WHERE where_condition);

type(连接类型):

Extra列值的含义:

Extra列包含了MySQL处理查询的一些额外信息,下面的列出了Extra中可能出现的值,如果你想让查询尽可能的快,应该注意下Extra字段中是否出现了using filesortusing temporary。下面只列除了在实际应用中经常会出现,相对比较重要的一部分,若描述的不够详细,可查看MySQL官方文档。

参考

MySQL官方文档

上一篇 下一篇

猜你喜欢

热点阅读