MySQL查询优化:explain解析

什么是EXPLAIN

使用EXPLAIN与sql语句一起使用,可以查看有关sql的执行计划。也就是说,MySQL解释了它将如何处理语句,包括有关如何连接表、以何种顺序连接、如何查询等信息。

image

EXPLAIN关键字一般放在SELECT查询语句的前面,用于描述MySQL如何执行查询操作、以及MySQL成功返回结果集需要执行的行数。explain 可以帮助我们分析 select 语句,让我们知道查询效率低下的原因,从而改进我们查询,让查询优化器能够更好的工作。

MySQL 查询优化器是如何工作的?

MySQL 查询优化器有几个目标,但是其中最主要的目标是尽可能地使用索引,并且使用最严格的索引来消除尽可能多的数据行。最终目标是提交 SELECT 语句查找数据行,而不是排除数据行。优化器试图排除数据行的原因在于它排除数据行的速度越快,那么找到与条件匹配的数据行也就越快。如果能够首先进行最严格的测试,查询就可以执行地更快。

EXPLAIN输出解析

MySQL 查询优化器有几个目标,但是其中最主要的目标是尽可能地使用索引,并且使用最严格的索引来消除尽可能多的数据行。最终目标是提交 SELECT 语句查找数据行,而不是排除数据行。优化器试图排除数据行的原因在于它排除数据行的速度越快,那么找到与条件匹配的数据行也就越快。如果能够首先进行最严格的测试,查询就可以执行地更快。

id:每个select子句的标识id

MySQL Query Optimizer 选定的执行计划中查询的序列号。表示查询中执行 select 子句或操作表的顺序,id 值越大优先级越高,越先被执行。id 相同,执行顺序由上至下

select_type:select查询的类型,主要是区别普通查询和联合查询、子查询之类的复杂查询

SIMPLE:简单SELECT(不使用UNION或子查询等)

PRIMARY:最外面的SELECT

UNION:UNION中的第二个或后面的SELECT语句

DEPENDENT UNION:UNION中的第二个或后面的SELECT语句,取决于外面的查询

UNION RESULT:UNION的结果。

SUBQUERY:子查询中的第一个SELECT

DEPENDENT SUBQUERY:子查询中的第一个SELECT,取决于外面的查询

DERIVED:导出表的SELECT(FROM子句的子查询)

table:输出的行所引用的表

显示的查询表名,如果查询使用了别名,那么这里显示的是别名,如果不涉及对数据表的操作,那么这显示为null,如果显示为尖括号括起来的就表示这个是临时表,后边的N就是执行计划中的id,表示结果来自于这个查询产生。如果是尖括号括起来的,与类似,也是一个临时表,表示这个结果来自于union查询的id为M,N的结果集

type【关键点】:联合查询所使用的类型

联合查询所使用的类型. type显示的是访问类型,是较为重要的一个指标,结果值从好到坏依次是:\
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL 一般来说,得保证查询至少达到range级别,最好能达到ref

possible_keys:查询可能使用到的索引都会在这里列出来

指出MySQL能使用哪个索引在该表中找到行。如果是空的,没有相关的索引。这时要提高性能,可通过检验WHERE子句,看是否引用某些字段,或者检查字段不是适合索引。

key:显示MySQL实际决定使用的索引

查询真正使用到的索引,如果没有索引被选择,则是NULL。select_type为index_merge时,这里可能出现两个以上的索引,其他的select_type这里只会出现一个。

key=primary的话,表示使用了主键;key=null表示没用到索引

key_len:显示MySQL决定使用的索引长度

显示MySQL决定使用的键长度。如果键是NULL,长度就是NULL。文档提示特别注意这个值可以得出一个多重主键里mysql实际使用了哪一部分。如果是单列索引,那就整个索引长度算进去,如果是多列索引,那么查询不一定都能使用到所有的列,具体使用到了多少个列的索引,这里就会计算进去,没有使用到的列,这里不会计算进去。留意下这个列的值,算一下你的多列索引总长度就知道有没有使用到所有的列了。要注意,mysql的ICP特性使用到的索引不会计入其中。另外,key_len只计算where条件用到的索引长度,而排序和分组就算用到了索引,也不会计算到key_len中。

ref:显示哪个字段或常数与key一起被使用

如果是使用的常数等值查询,这里会显示const,如果是连接查询,被驱动表的执行计划这里会显示驱动表的关联字段,如果是条件使用了表达式或者函数,或者条件列发生了内部隐式转换,这里可能显示为func

rows:显示MySQL认为它执行查询时必须检查的行数

这个数表示mysql要遍历多少数据才能找到,在innodb上是不准确的。这里是执行计划中估算的扫描行数,不是精确值

filtered:查询记录数量的百分比

这个字段表示存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量的比例,注意是百分比,不是具体记录数。

Extra【关键点】:包含MySQL解决查询的详细信息,也是关键参考项之一

type:联合查询所使用的类型

联合查询所使用的类型 type 显示的是访问类型,是较为重要的一个指标,结果值从好到坏依次是:\
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL 一般来说,得保证查询至少达到range级别,最好能达到ref

system

表仅有一行(=系统表)。这是 const 连接类型的一个特例。

const

将PRIMARY KEY或UNIQUE索引的所有部分与常量值进行比较时使用const。使用唯一索引或者主键,返回记录一定是1行记录的等值where条件时,通常type是const。

eq_ref

出现在要连接过个表的查询计划中,驱动表只返回一行数据,且这行数据是第二个表的主键或者唯一索引,且必须为not null,唯一索引和主键是多列时,只有所有的列都用作比较时才会出现eq_ref

ref

不像eq_ref那样要求连接顺序,也没有主键和唯一索引的要求,只要使用相等条件检索时就可能出现,常见与辅助索引的等值查找。或者多列主键、唯一索引中,使用第一个列之外的列作为等值查找也会出现,总之,返回数据不唯一的等值查找就可能出现

fulltext

全文索引检索,要注意,全文索引的优先级很高,若全文索引和普通索引同时存在时,mysql不管代价,优先选择使用全文索引

注意

在MySQL数据库中,有四种索引:聚集索引(主键索引)、普通索引、唯一索引和全文索引,全文索引(也称全文检索)是目前搜索引擎使用的一种关键技术。它能够利用分词技术等多种算法智能分析出文本文字中关键字词的频率及重要性,然后按照一定的算法规则智能地筛选出我们想要的搜索结果

ref_or_null

与ref方法类似,只是增加了null值的比较。实际用的不多

index_merge

表示查询使用了两个以上的索引,最后取交集或者并集,常见and ,or的条件使用了不同的索引,官方排序这个在ref_or_null之后,但是实际上由于要读取所个索引,性能可能大部分时间都不如range

unique_subquery

用于where中的in形式子查询,子查询返回不重复值唯一值

index_subquery

用于in形式子查询使用到了辅助索引或者in常数列表,子查询可能返回重复值,可以使用索引将子查询去重

range

索引范围扫描,常见于使用>,<,is null,between ,in ,like等运算符的查询中

index

索引全表扫描,把索引从头到尾扫一遍,常见于使用索引列就可以处理不需要读取数据文件的查询、可以使用索引排序或者分组的查询。主要优点就是避免了排序, 但是开销仍然非常大

all

最坏的情况,从头到尾全表扫描

Extra:额外信息

这个列可以显示的信息非常多,有几十种,是执行计划的额外信息,下面举出几个常见的

Distinct

一旦MYSQL找到了与行相联合匹配的行,就不再搜索了

Not exists

MYSQL 优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的行,就不再搜索了

Range checked for each Record

MySQL发现没有好的索引可以使用,但发现在前面的表的列值已知之后可能会使用某些索引。
对于上表中的每个行组合,MySQL检查是否可以使用range或index_merge访问方法来检索行。这是使用索引的最慢的连接之一

Using index

查询时不需要回表查询,直接通过索引就可以获取查询的数据,效率不错

Using where

除非您特别打算从表中获取或检查所有行,否则如果没有使用where,并且表联接类型为all或index,那么您的查询可能会出错。

Using filesort【需要优化】

看到这个的时候,查询就需要优化了。MYSQL需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行。当Query 中包含 ORDER BY 操作,而且无法利用索引完成排序操作的时候,MySQL Query Optimizer 不得不选择相应的排序算法来实现

Using temporary【需要优化】

看到这个的时候,查询就需要优化了。在解析查询并排序时,MySQL需要创建一个临时表来保存结果。如果查询包含以不同方式列出列的GROUP BY和ORDER BY子句,则通常会发生这种情况。

注意

如果此信息显示Using filesort或者Using temporary的话会很吃力,WHERE和ORDER BY的索引经常无法兼顾,如果按照WHERE来确定索引,那么在ORDER BY时,就必然会引起Using filesort,这就要看是先过滤再排序划算,还是先排序再过滤划算。

参考文献