你的SQL为什么慢?看懂MySQL EXPLAIN执行计划,快速定位性能瓶颈

28 阅读6分钟

当一条SQL慢的时候需要分析具体原因,基本第一反应就是祭出EXPLAIN命令去解析这条SQL执行情况。但是大多数人都只会看是否命中索引以及对表的访问类型。是否看到Extra里的Using filesortUsing temporary只知道是警告,却不知如何解决?下面就带你详细分析EXPLAIN解释结果的每一列都分别代表着什么意思。

id

select语句的执行序号,执行顺序为从大到小执行,如果相同时从上往下执行。

select_type

  1. SIMPLE:简单的select,不使用UNION或者子查询等;
  2. PRIMARY:子查询中最外层查询,查询中若包含任何复杂的子部分,最外层的select被标记为PRIMARY;
  3. UNION:UNION中的第二个或者后面的select语句;
  4. DEPENDENT UNION:UNION中的第二个或者后面的select语句,取决于外面的查询;
  5. UNION RESULT:UNION的结果;
  6. SUBQUERY:子查询中的第一个select,结果不依赖于外部查询;
  7. DEPENDENT SUBQUERY:子查询中的第一个select,结果依赖于外部查询;
  8. DERIVED:派生表的select,from自居的子查询;
  9. UNCACHEABLE SUBQUERY:一个子查询的结果不能被缓存,必须重新评估外连接饿第一行

table

显示这一步所访问数据库中表名称(显示这一行的数据是关于哪张表的),有时不是真实的表名称,可能是别名,也可能是第几步执行的结果的简称

type

对表的访问方式,表示MySQL在表中找到所需行的方式,又称“访问类型”。

  1. ALL:全表扫描,性能最差,如果在查询中使用了limit
  2. index:跟ALL一样,也会进行全表扫描,只是MySQL会按索引次序进行全表扫描,而不是直接扫描行数据。它主要的优点是避免了排序;最大的缺点是要承担按索引次序读取整个表的开销。若是按随机次序访问行,开销将会非常大。
  3. range:范围扫描,就是一个有范围限制的索引扫描。它开始于索引里的某一点,返回批匹配这个范围值的行。range比全索引扫描更高效,因为它用不着遍历全部索引。
  4. ref:标识上述表的连接匹配条件,即哪些列或常量被用于查找索引列商得值。
  5. eq_ref:类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary_key或者unique_key作为关联条件。
  6. const、system:当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。例如主键至于where列表中,mysql就能将该查询转换为一个常量,system是const类型的特例,当查询的表只有一行的情况下,使用system。
  7. null:MySQL在优化过程中分解语句,执行时甚至不用访问表或者索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。

possible_keys

指出MySQL能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用(该查询可以利用索引,如果没有任何索引显示null)该列完全独立于explain输出所示的表的次序。这意味着在possible_keys中的某些键实际上不能按生成的表次序使用,如果该列是null,则没有相关的索引。在这种情况下,可以通过检查where自居看是否它引用某些列或者适合索引的列来提高你的查询性能。如果是这样,创造一个适当的索引并且再次用explain检查查询。

key

key列显示MySQL实际决定使用的键(索引),必然包含在possible_keys中如果没有选择索引,键是NULL。要想强制MySQL 使用或忽视possible_keys列中的索引,在查询中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。

key_len

表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度(key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的)不损失精确性的情况下,长度越短越好

ref

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

rows

估算出结果集行数,表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数

extra

该列包含MySQL解决查询的详细信息,下面详细。

  1. Distinct:一旦MYSQL找到了与行相联合匹配的行,就不再搜索了。
  2. Not exists:MYSQL优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的行,就不再搜索了
  3. Range checked for each:Record(index map:#)没有找到理想的索引,因此对于从前面表中来的每一个行组合,MYSQL检查使用哪个索引,并 用它来从表中返回行。这是使用索引的最慢的连接之一
  4. Using filesort:看到这个的时候,查询就需要优化了。MYSQL需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键 值和匹配条件的全部行的行指针来排序全部行
  5. Using index:列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候
  6. Using temporary:看到这个的时候,查询需要优化了。这里,MYSQL需要创建一个临时表来存储结果,这通常发生在对不同的列集进行ORDER BY 上,而不是GROUP BY上
  7. Using where:使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行,并且连接类型ALL或index,这 就会发生,或者是查询有问题

注意

  1. EXPLAIN不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响情况
  2. EXPLAIN不能显示MySQL在执行查询时所作的优化工作
  3. 部分统计信息是估算的,并非精确值
  4. EXPALIN只能解释SELECT操作,其他操作要重写为SELECT后查看执行计划。