使用EXPLAIN关键字可以模拟优化器执行SQL语句,从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是结构的性能瓶颈
image.png

  • 表的读取顺序
  • 数据读取操作的操作类型
  • 哪些索引可以使用
  • 哪些索引被实际使用
  • 表之间的引用
  • 每张表有多少行被优化器查询

    一、ID

    select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序

  • id相同,执行顺序由上至下

  • id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
  • id相同不同,同时存在。id相同可认为是同一组,所有组中,id越大,优先级越高

    二、select_type

    1.simple

    简单的select查询,查询中不包含子查询或者UNION

    2.primary

    查询中若包含任何复杂的子部分,最外层查询则被标记为primary

    3.subquery

    如果包含子查询的查询语句不能够转为对应的 semi-join 的形式,并且该子 查询是不相关子查询,并且查询优化器决定采用将该子查询物化的方案来执行该 子查询时,该子查询的第一个 SELECT 关键字代表的那个查询的 select_type 就是 SUBQUERY

  • 是一个不相关子查询

  • 不能semi-join (半连接优化技术)
  • 物化表 (结果缓存内存,临时表)

    4.derived 衍生

  • 用于 from 子句里有子查询的情况。 MySQL 会递归执行这些子查询, 把结果放在临时表里。

  • 对于采用物化的方式执行的包含派生表的查询,该派生表对应的子查询的 select_type 就是 DERIVED。

    5.union

    对于包含 UNION 或者 UNION ALL 的大查询来说,它是由几个小查询组成的, 其中除了最左边的那个小查询以外,其余的查询的 select_type 值就是 UNION

    6.union result

    MySQL 选择使用临时表来完成 UNION 查询的去重工作,针对该临时表的查 询的 select_type 就是 UNION RESULT

    7.dependent union

    UNION 中的第二个或随后的 select 查询,依赖于外部查 询的结果集

    8.dependent subquery

    子查询中的第一个 select 查询,依赖于外部查询的 结果集

    9.materialized

    物化子查询

    10.uncacheable subquery

    结果集不能被缓存的子查询,必须重新为外层查 询的每一行进行评估,出现极少。

    11.uncacheable union

    UNION 中的第二个或随后的 select 查询,属于不可缓存的子查询,出现极少。
    :::tips

  • semi-join:半连接优化技术,本质上是把子查询上拉到父查询中,与父查询的表做 join 操作。关键词是“上拉”。对于子查询,其子查询部分相对于父表的每个符合条件的元组,都要把子查询执行一轮。效率低下。用半连接操作优化子查询,是把子查询上拉到父查询中,这样子查询的表和父查询中的表是并列关系,父表的每个符合条件的元组,只需要在子表中找符合条件的元组即可。简单来说,就是通过将子查询上拉对父查询中的数据进行筛选,以使获取到最少量的足以对父查询记录进行筛选的信息就足够了。

  • 子查询物化:子查询的结果通常缓存在内存或临时表中。
  • 关联/相关子查询:子查询的执行依赖于外部查询。多数情况下是子查询的WHERE 子句中引用了外部查询的表。自然“非关联/相关子查询”的执行则不依赖与外部的查询。 :::

    三、table

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

    四、type

    显示查询使用了何种类型
    从最好到最差依次是: :::success system>const>eq_ref**>ref>range**>index>ALL ::: 一般来说,得保证查询至少**达到range级别,最好达到ref**

    1.system

    表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,这个也可以忽略不计

    2.const

    表示通过索引一次就找到了,const用于比较primary key或者unique索引。因为只匹配一行数据,所以很快。如将主键置 于where列表中,MySQL就能将该查询转换为一个常量

    3.eq_ref

  • 唯一性索引,对于每个索引键,表中只有一条记录与之匹配,常见于主键或唯一索引扫描

  • 连接查询时,如果被驱动表通过主键或者唯一二级索引列等值匹配的方式进行访问的(如果该主键或者唯一二级索引是联合索引的话,所有的索引列都必须进行等值比较),则对该被驱动表的访问方法就是 eq_ref :::tips

    驱动表与被驱动表

    A 表和 B 表 join 连接查询,如果通过 A 表的结果集作为 循环基础数据,然后一条一条地通过该结果集中的数据作为过滤条件到 B 表中查 询数据,然后合并结果。那么我们称 A 表为驱动表,B 表为被驱动表.
    比方说:
    image.png
    从执行计划的结果中可以看出,MySQL 打算将 s2 作为驱动表,s1 作为被驱动表,重点关注 s1 的访问方法是 eq_ref,表明在访问 s1 表的时候可以通过主键 的等值匹配来进行访问。 :::

    4.ref

  • 非唯一索引的扫描,返回匹配某个单独值的所有行。

  • 当通过普通的二级索引列与常量进行等值匹配时来查询某个表,那么对该 表的访问方法就可能是 ref。 本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,它可能 会找到多个符合条件的行,所以他属于查找和扫描的混合体

    5.fulltext

    全文索引

    6.ref_or_null

    普通索引的常数项和null项

    7.index_merge 索引合并

    索引合并

    8.unique_subquery 唯一索引子查询

    类似于两表连接中被驱动表的 eq_ref 访问方法,unique _subquery 是针对在 一些包含IN子查询的查询语句中,如果查询优化器决定**将IN子查询转换为EXISTS子查询**,而且子查询可以使用到主键进行等值匹配的话,那么该子查询执行计划 的 type 列的值就是 unique_subquery

    9.index_subquery 普通索引子查询

    index_subquery 与 unique_subquery 类似,只不过访问⼦查询中的表时使⽤的是普通的索引

    10.range 范围查询

    只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引。一般就是在你的where语句中出现 between、<、>、in等的查询。这种范围扫描索引扫描比全表扫描要好,因为他只需要开始索引的某一点,而结束语另一点,不用扫描全部索引

    11.index 索引覆盖

    Full Index Scan,index与ALL区别为index类型只遍历索引树。这通常比ALL快,因为索引文件通常比数据文件小。
    也就是说虽然all和index都是读全表,但index是从索引中读取的,而all是从硬盘中读的

    12.ALL

    FullTable Scan,将遍历全表以找到匹配的行

五、possible_keys

显示可能应用在这张表中的索引,一个或多个。
查询涉及的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用

六、key

实际使用的索引。如果为null则没有使用索引
查询中若使用了覆盖索引,则索引和查询的select字段重叠(索引仅出现在key列表中

七、key_len

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

八、ref

显示索引哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值

九、rows

根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数,越小越好

十、Extra

包含不适合在其他列中显示但十分重要的额外信息

1.Using filesort

说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中无法利用索引完成排序操作成为“文件排序”。
出现这个请尽快优化
image.png

2.Using temporary

使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于distinct、排序order by 和分组查询 group by
出现这个表示很严重了,急需优化
image.png

3.Using index

表示相应的select操作中使用了覆盖索引(Coveing Index),避免访问了表的数据行,表示**效率不错**!
如果同时出现using where,表明索引被用来执行索引键值的查找;
如果没有同时出现using where,表面索引用来读取数据而非执行查找动作。
两种理解方式,建议为第一种,不再去读取数据文件。
image.png

4.Using where

表面使用了where过滤

5.using join buffer

在连接查询执行过程中,当被驱动表不能有效的利用索引加快访问速度, MySQL 一般会为其分配一块名叫 join buffer 的内存块来加快查询速度

6.impossible where

where子句的值总是false,不能用来获取任何元组

7.select tables optimized away

在没有GROUPBY子句的情况下,基于索引优化MIN/MAX操作或者对于MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化。

8.distinct

优化distinct,在找到第一匹配的元组后即停止找同样值的工作

9.Using index condition 索引条件下推

有些搜索条件中虽然出现了索引列,但却不能使用到索引,比如下边这个查询:

SELECT * FROM s1 WHERE order_no > ‘z’ AND order_no LIKE ‘%a’;

其中的 order_no> ‘z’可以使用到索引,但是 order_no LIKE ‘%a’却无法使用到索引,在以前版本的 MySQL 中,是按照下边步骤来执行这个查询的:
1、先根据 order_no> ‘z’这个条件,从二级索引 idx_order_no 中获取到对应的二级索引记录。
2、根据上一步骤得到的二级索引记录中的主键值进行回表(因为是 select ), 找到完整的用户记录再检测该记录是否符合 key1 LIKE ‘%a’这个条件,将符合条件的记录加入到最后的结果集。
但是虽然 **order_no LIKE ‘%a’不能组成范围区间参与 range 访问方法的执行, 但这个条件毕竟只涉及到了 order_no 列*
,MySQL 把上边的步骤改进了一下。

索引条件下推

1、先根据 order_no> ‘z’这个条件,定位到二级索引 idx_order_no 中对应的二级索引记录。
2、对于指定的二级索引记录,先不着急回表,而是先检测一下该记录是否满足 order_noLIKE ‘%a’这个条件,如果这个条件不满足,则该二级索引记录压根儿就没必要回表
3、对于满足 order_no LIKE ‘%a’这个条件的二级索引记录执行回表操作。我们说回表操作其实是一个随机 IO,比较耗时,所以上述修改可以省去很多回表操作的成本。这个改进称之为索引条件下推(英文名:ICP ,Index Condition Pushdown)。
如果在查询语句的执行过程中将要使用索引条件下推这个特性,在 Extra 列中将会显示 Using index condition