explain 深入剖析 MySQL 索引及其性能优化指南

时间:2022-04-28
本文章向大家介绍explain 深入剖析 MySQL 索引及其性能优化指南,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

0.SQL标准的执行流程(select)

(8)  SELECT
(9)  DISTINCT
(11) <TOP_specification> <select_list>
(1)  FROM <left_table>
(3)  <join_type> JOIN <right_table>
(2)  ON <join_condition>
(4)  WHERE <where_condition>
(5)  GROUP BY <group_by_list>
(6)  WITH {CUBE ROLLUP}
(7)  HAVING <having_condition>
(10) ORDER BY <order_by_list>
  1. FROM:对FROM子句中的前两个表执行笛卡尔积,生成虚拟表VT1
  2. ON:对VT1应用ON筛选器。只有那些使<join_condition>为真的行才被插入VT2
  3. OUTER(JOIN):如果指定了OUTER JOIN,保留表中未找到匹配的行将作为外部行添加到VT2,生成VT3。如果FROM子句包含两个以上的表,则对上一个联接生成的结果表和下一个表重复执行步骤1到步骤3,直到处理完所有的表为止
  4. 对VT3应用WHERE筛选器。只有使<where_condition>为TRUE的行才被插入VT4
  5. GROUP BY:按GROUP BY 子句中的列列表对VT4中的行分组,生成VT5
  6. CUBEROLLUP:把超组插入VT5,生成VT6。
  7. HAVING:对VT6应用HAVING筛选器。只有使<having_condition>为TRUE的组才会被插入VT7
  8. SELECT:处理SELECT列表,产生VT8。
  9. DISTINCT:将重复的行从VT8中移除,产生VT9
  10. ORDER BY:将VT9中的行按ORDER BY子句中的列列表排序,生成一个有表(VC10)
  11. TOP:从VC10的开始处选择指定数量或比例的行,生成表VT11,并返回给调用者

1.id:SQL执行的顺利的标识。

首先 sql 从里向外执行,而 id是一组数字,表示查询中执行select子句或操作表的顺序。

如果id相同,则执行顺序从上至下。

如果是子查询,id的序号会递增,id越大则优先级越高,越先会被执行。

id如果相同,则可以认为是一组,从上往下顺序执行,所有组中,id越高,优先级越高,越容易执行。

2.select_type:SELECT类型

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

2) PRIMARY:最外层的select

3)DERIVED:派生表的SELECT(FROM子句的子查询)

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

5)UNION RESULT:UNION的结果。

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

7)SUBQUERY:子查询中的第一个SELECT

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

PS:这里我总结了下子查询的in语句会用到DEPENDENT关键字,如果子查询是union则是DEPENDENT UNION;如果子查询是简单的条件语句则是DEPENDENT SUBQUERY。这里不一定准确是我自己总结的哈~~如果不对望指正

3.table:表的名字。

有时不是真实的表名字,看到的是derivedx(x是个数字,我的理解是第几步执行的结果)

4.type:连接操作的类型。

MySQL执行计划EXPLAIN主要可以通过type来进行分析:

type表示MySQL在表中找到所需行的方式,又称“访问类型”,常见类型如下:(从上至下,效果依次变好)

  ALL:Full Table Scan。 index:Full Index Scan。

  range:索引范围扫描。

  ref :非唯一性索引扫描。

  eq_ref :唯一性索引扫描。

  const,system:将查询转换为一个常量。

  null:MySQL在优化过程中分解语句,执行时甚至不用访问表或索引

  ALL:ALL 表示”全表扫描”(full table scan), 性能是最差的几种查询之一,如果查询的表比较大,且查询频次高,对MySQL数据库有致命的性能影响。示例如下:

  ename字段上没有索引,所以也是全表扫描。

  index: index 表示“全索引扫描”(full index scan),其类型和ALL较类似,性能也是比较差; 和ALL区别在于只对索引树进行扫描,但索引没有起到过滤作用。

  ID字段为主键索引,在索引中扫描。

  range:索引范围扫描,对索引的扫描开始于某一点,返回匹配值域的行, 常见于between、<、>,IN等的查询

  ref:针对于非唯一或主键索引,或使用二者”最左部分字段”索引的等值查询或多表join,查询效率由这个值返回的行数多少决定。

  name和age和覆盖索引,这里只使用了name,即只使用了唯一性索引的一部分,故为ref。

  eq_ref:eq_ref 使用于多表的join时,被驱动表的过滤字段是主键或唯一索引,查询效率很好。

  MID对于表Manager是唯一的,主键索引,来与employee连接,故type为eq_ref。

  const、system:const 针对主键或唯一索引的等值查询扫描,最多只返回一行数据。

  system是const类型的特例,当查询的表只有一行的情况下, 使用system。

  exployee中ID为主键索引,可以直接定位,故为const,而衍生后的结果集A中只有一条记录,故为type为system。

  NULL:MySQL在优化过程中分解语句,执行时甚至不用访问表或索引。

  ID= SELECT MIN(ID)说明ID已确定,使用EXPLAIN EXTENDED优化,所以type为null,mysql自动优化,无需去访问表或索引。

  使用show warnings查看优化后得到的结果。

  index_merge:MySQL查询优化器发现查询可以同时使用多个索引查询结果集进行并集或交集的情况,就会使用index_merge type。此时key字段有两个或多个索引, key_len/rows都分别有两个数值; 如果是并集操作”Using intersect”, 往往通过两个索引的字段,合并为一个索引,避免index_merge查询 下图中两个SQL一个是AND/OR, Using intersect 和Using union 分别表示使用两个索引后的交集和并集

5.possible_keys:MySQL在搜索数据记录时可以选用的各个索引的名字。

表示MySQL查询优化器发现当前查询可能被使用地索引,但不一定能会利用,如果possible_key的列举的索引越多,往往说明索引创建不合理,查询效率不是最高效; 因为优化器会分析尽可能多的索引,评估哪个索引的“成本”消耗局部最低,这个评估过程消耗时间和资源的。

6.key:它显示了MySQL实际使用的索引的名字。

key数据列是MySQL实际选用的索引,如果它为空(或NULL),则MySQL不使用索引。

7.key_len:索引中被使用部分的长度,以字节计。

key_len的值可以告诉你在联合索引中mysql会真正使用了哪些索引。 在上例中,key_len是102,其中firstname占50字节,lastname占50字节,age占2字节(smallint存储大小为2字节)。如果MySQL只使用索引中的firstname部分,则key_len将是50。 在不损失精确性的情况下 ,key_len数据列里的值越小越好(意思是更快)。

8.ref:显示使用哪个列或常数与key一起从表中选择行。

ref数据列给出了关联关系中另一个数据表里的数据列的名字。

9.rows:MySQL所认为的它在找到正确的结果之前必须扫描的记录数。

MySQL查询优化器根据统计信息,估算SQL要查找到结果集需要扫描读取的数据行数; 这个值非常直观显示SQL的效率好坏,原则rows越少越好。显然,这里最理想的数字就是1。

10.extra:附加信息

Using index和Using where会遇到的比较多,可以重点记下,其他的我没怎么遇到过了解即可,遇到具体问题可以查阅哈

1)Distinct 

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

2)Not exists 

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

3)Range checked for each 

没有找到理想的索引,因此对于从前面表中来的每一个行组合,MYSQL检查使用哪个索引,并用它来从表中返回行。这是使用索引的最慢的连接之一 

4)Using filesort 

MySQL需额外的排序操作,不能通过索引顺序达到排序效果;又叫”文件排序“,易错误理论为排序结果过大,内存中不够需写磁盘文件排序。 一般有filesort,都建议优化去掉,CPU资源消耗大。 下图last_update排序,但此字段无索引,故需filesort

5)Using index 

”覆盖索引扫描“,表示查询在索引树中就可查找所需数据,不用回表数据文件(回表操作),往往说明性能不错,这发生在对表的全部的请求列都是同一个索引的部分的时候

6)Using temporary 

看到这个的时候,查询需要优化了。查询有使用临时表,一般出现于排序,分组和多表join的情况,查询效率不高,建议优化。  

7)Using where

使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行,并且连接类型ALL或index,这就会发生,或者是查询有问题

先说到这,下面一篇给大家总结下如何选择索引列以及使用索引的注意事项。

简要解释版本

EXPLAIN列的解释:

描述

table

显示这一行的数据是关于哪张表的。

type

这是重要的列,显示连接使用了何种类型。从最好到最差的连接类型为 const、eq_reg、ref、range、index和ALL。

possible_keys

显示可能应用在这张表中的索引。如果为空,没有可能的索引。可以为相关的域从WHERE语句中选择一个合适的语句。

key

实际使用的索引。如果为NULL,则没有使用索引。很少的情况下,MySQL会选择优化不足的索引。这种情况下,可以在SELECT语句中使用USE INDEX(indexname) 来强制使用一个索引或者用IGNORE INDEX(indexname)来强制MySQL忽略索引。

key_len

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

ref

显示索引的哪一列被使用了,如果可能的话,是一个常数。

rows

MySQL认为必须检查的用来返回请求数据的行数。

Extra

关于MySQL如何解析查询的额外信息。将在表4.3中讨论,但这里可以看到的坏的例子是Using temporary和Using filesort,意思MySQL根本不能使用索引,结果是检索会很慢。

extra列返回的描述的意义:

意义

Distinct

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

Not exists

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

Range checked for each Record(index map:#)

没有找到理想的索引,因此对于从前面表中来的每一个行组合,MySQL检查使用哪个索引,并用它来从表中返回行。这是使用索引的最慢的连接之一。

Using filesort

看到这个的时候,查询就需要优化了。MySQL需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行。

Using index

列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候。

Using temporary

看到这个的时候,查询需要优化了。这里,MySQL需要创建一个临时表来存储结果,这通常发生在对不同的列集进行ORDER BY上,而不是GROUP BY上。

Where used

使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行,并且连接类型ALL或index,这就会发生,或者是查询有问题不同连接类型的解释(按照效率高低的顺序排序)。

system

表只有一行 system 表。这是const连接类型的特殊情况 。

const

表示通过主键或惟一索引一次就找到了,查找时间为 O(1),可以认为是个常数(constant),所以叫 const;

eq_ref

在 join 的时候,对于每个索引键,表中只有唯一一条记录与之匹配,简单来说就是多表连接中使用primary key或者 unique key作为关联条件;

ref

与 eq_ref 区别是用了非唯一索引扫描;

range

这个连接类型使用索引返回一个范围中的行,比如使用>或<查找东西时发生的情况。

index

这个连接类型对前面的表中的每一个记录联合进行完全扫描(比ALL更好,因为索引一般小于表数据)。

ALL

这个连接类型对于前面的每一个记录联合进行完全扫描,这一般比较糟糕,应该尽量避免。

REF:

[1] 细说MySQL Explain和Optimizer Trace简介

https://zhuoroger.github.io/2016/08/11/mysql-explain/

[2] 如何理解 MySQL 的执行计划

http://blog.decaywood.me/2017/04/07/the-execution-plan-of-MySQL/