vlambda博客
学习文章列表

深入剖析MySQL中的Explain

阅读前,请点击上面蓝色字体“JAVA首席架构师”,再点击“关注”,这样您就可以及时收到文章发布通知。


Explain相信大家都不陌生,Explain命令是查看查询优化器如何决定执行查询的主要方法。

要使用Explain也很简单,只需要在查询中的SELECT关键字之前增加Explain这个词即可。

例如:

EXPLAIN SELECT * FROM tb_user WHERE id = 1;

Explain中的列

Explain出来的信息有10列,分别是id、select_type、table、type、possible_keys、key、key_len、ref、rows、Extra。


id 

这一列总是包含一个编,表示SELECT所属的行。


如果在语句当中没有自查询或者联合查询,那么只会有唯一的SELECT,所以每一行在这个列中都会显示一个1。


否则,内层的SELECT语句会顺序编号,对应于其在原始语句中的位置。


id的结果会出现3种情况:

  • id都相同:由上至下顺序执行。

  • id都不同:如果是子查询,id会递增,id值越大,优先级越高,越先被执行。

  • id相同不同,同时存在:id如果相同,可以当成一组,由上至下顺序执行。在所有组中,id越大,优先级越高,越先执行。


MySQL将SELECT查询分为简单和复杂类型,复杂类型可分为三大类:简单子查询、所谓的派生表(在FROM字句中的子查询),以及UNION查询。


select_type 

一列显示了对应行是简单还是复杂SELECT。


SIMPLE值意味着查询不包括子查询和UNION。


如果查询有任何复杂的子部分,则最外层部分标记为PRIMARY,其他部分标记如下。


SUBQUERY:包含在SELECT列表中的子查询中的SELECT(换句话说,不再FROM字句中)标记为SUBQUERY。


DERIVED:DERIVED值用来表示包含在FROM子句的子查询中SELECT,MySQL会递归执行并将结果放到一个临时表中。

服务器内部称其“派生表”,因为该临时表是从子查询中派生来的。


UNION:在UNION中的第二个和随后的SELECT被标记为UNION,第一个SELECT被标记就好像它以部分外查询来执行。

如果UNION被FROM子句中的子查询包含,那么它的第一个SELECT会被标记为DERIVED。


UNION RESULT:用来从UNION的匿名临时表检索结果的SELECT被标记为UNION RESULT。


除了这些值,SUBQUERY和UNION还可以被标记为DEPENDENT和UNCACHEABLE。


DEPENDENT意味着SELECT依赖于外层查询中发现的数据。

UNCACHEABLE意味着SELECT中的某些特性组织结果被缓存于一个Item_cache中。


table 

一列显示了对应行正在访问哪个表,或是该表的别名(如果SQL中定义了别名)。


type 

我们称之为“访问类型”,也就是MySQL决定如何查询表中的行。

访问类型有ALL、index、range、ref、eq_ref、const、system、NULL,依次从最差到最优。


  • ALL:这就是我们常说的全表扫描,通常意味着MySQL必须扫描整张表,从头到尾,去找到需要的行。

  • index:这个跟全表扫描一样,只是MySQL扫描表时按索引次序进行而不是行。主要优点是避免了排序。最大的缺点是要承担按索引次序读取整个表的开销,这意味着若是按随机次序访问行,开销将会非常大。如果在Extra列中看到“Using index”,说明MySQL正在使用覆盖索引,它只扫描索引的数据,而不是按索引次序的每一行。它比索引次序全表扫描的开销要少很多。

  • range:范围扫描就是一个有效值的索引扫描,它开始于索引里的某一点,返回匹配这个值域的行。这比全索引扫描好一些,因为它用不着遍历全部索引。

  • ref:这是一种索引访问(也叫索引查找),它返回所有匹配某个单个值的行。它也可能会找到多个符合条件的行,因此,它是查找和扫描的混合体。ref索引访问类型只有当使用非唯一索引或者唯一性索引的非唯一前缀时才会发生。把它叫做ref,是因为索引要跟某个参考值比较。这个参考值可能是一个常数,或者是来自多表查询前一个表里的结果值。

  • eq_ref:使用这种索引查找,MySQL知道最多只返回一条符合条件的记录。这种访问方法可以在MySQL使用主键或者唯一性索引查找时看到,他会将他们与某个参考值做比较。

  • const,system:当MySQL能对查询的某部分进行优化并将其转换成一个常量时,它就会使用这些访问类型。

  • NULL:这种访问方式意味MySQL能在优化阶段分解查询语句,在执行阶段甚至用不着再访问表或者索引。例如,从一个索引列里选取最小值,可以通过单独查找索引来完成,不需要再执行时访问表。


possible_keys 

这一列显示了查询可以使用哪些索引,这是基于查询访问的列和使用的比较操作符来判断的。


key 

这一列显示了MySQL真实使用的哪个索引来优化对该表的访问。


possible_keys告诉了哪一个索引能高效进行查询,而key是MySQL认为使用该索引可以最大化的降低查询成本。


如果没有使用索引,值是NULL。


有时候MySQL使用的并不是我们期望的索引,在查询中使用FORCE INDEX、USE INDEX或者IGNORE INDEX来变更使用的索引。


需要注意的是,MySQL真正使用的索引,也就是key列的值,不一定必须是possible_keys中的值,这点需要注意。


比如我的表结构如下,id为主键,name为普通索引。

CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(50) DEFAULT NULL COMMENT '用户姓名', `email` varchar(50) DEFAULT NULL COMMENT '用户邮箱', PRIMARY KEY (`id`), KEY `idx_name` (`name`) USING BTREE) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='用户表'

执行SQL:

EXPLAIN SELECT `name` FROM `user`;

执行计划如图:

我们可以看到,possible_keys为NULL,但是key列为idx_name,Extra为Using index,可以看到它选择了覆盖索引,哪怕没有where子句。


key_len 

这一列显示了MySQL在索引里使用的字节数。


key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的。


在不损失精确性的情况下,长度越短越好。


ref 

列与索引的比较,表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值。


rows 

这一列显示的是MySQL预估为了找到所需的行而要读取的行数。

当然,这个rows值越小越好。


Extra 

这一列显示的是不适合在其他列显示的额外信息。

我们常见的最重要的值有:

  • Using index:表示MySQL将使用覆盖索引,以避免回表。

  • Using where:表示MySQL服务器将在存储引擎检索行后再进行过滤。许多WHERE条件里涉及索引中的列,当它读取索引时,就能被存储引擎检验,因此不是所有带WHERE子句的查询都会显示“Using where”。

  • Using temporary:表示MySQL在对查询结果排序时会使用一个临时表。如果查询包含不同列的GROUP BY和ORDER BY子句,则通常会发生这种情况。

  • Using filesort:表示MySQL会对结果使用一个外部索引排序,而不是按索引次序从表里读取行。这类SQL语句性能极差,需要进行优化。

  • Range checked for each record (index map: N):表示没有好用的索引,新的索引将在联接的每一行上重新估算。N是显示在possible_keys列中索引的图,并且是冗余的。

  • No tables used:表示没有FROM子句或者使用DUAL虚拟表

  • Using join buffer:强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能。



如果您喜欢,请分享一下让更多的人学习

长按下方的二维码添加关注



你点的每个 ,都是对我最大的鼓励