我们所有的查询语句,MySQL 都会为其选择一个最合适的执行计划。这个执行计划就展示了接下来执行查询的具体方式。在日常工作中我们可以在 SQL 语句前面加上 EXPLAIN 关键字来查看具体的执行计划。

举个例子:

image.png

这种就是我们日常用到 EXPLAIN 看到的最直接结果,也是这个查询语句最终的执行计划。

执行计划输出中各列详解

这个笔记就是用来解释这个执行计划中的各个列分别对应的是什么意思。不过在整理具体之前,先简明扼要的说一下每个字段的具体含义:

列名描述备注
id在一个大的查询中,每个 SELECT 对应一个唯一的 idid 小的先执行
select\_typeSELECT 关键字对应的查询类型连接查询和子查询的时候才有用
partitions匹配的分区信息
type针对单表的访问方式我们最常用的字段
possible\_Keys可能用到的索引
key实际使用的索引
key\_len实际使用的索引长度
ref当使用索引列等值查询时,与索引列等值匹配的对象信息
rows预估的需要读取的记录条数
filtered针对预估的需要读取的记录,经过搜索条件过滤后剩余记录条数的百分比rows 和 filtered 在判断连接查询的扇出的时候,是一个非常重要的判断指标。
Extra一些额外的信息重点

id 列

通常来说一个查询语句都有一个或多个 SELECT ,在执行计划中每一个 SELECT 都会被单独分配一个 id。为方便理解,我们举几个简单的例子:

image.png

这种单表等值查询,显而易见的只有一个 SELECT 而且实际上也只访问了一张表,所以下面的 id 只有一个是 1。这种情况下其实我们是无法判断这个 id 到底是因为 SELECT 关键字单独分配的,还是根据表单独分配的。所以这里,我们需要再来看一个连接的查询的情况。这里其实无论是内查询还是外查询都是一样的,我在这里举了一个外查询的例子。

image.png

从结果上我们可以看到,这里有两个表分别是 s1 和 s2,但是他们还是只分配了同样的 id,这样就可以证实 id 列的值是根据 SELECT 分配的了吗?本着不试不爽的态度,我们再来看一个 UNION 子句的情况。

image.png

从上面这三个例子中,我们就可以证实执行计划中每一个 SELECT 都会被单独分配一个 id。相信在看 UNION 这个例子的执行计划中,你或许感到一点奇怪。为什么我这是两个表的查询结果的 UNION ,怎么在执行计划中出现了第三行,而且这一行还这么奇怪,id 是 NULL

这个其实是一个内部的临时表,MySQL 为了让 id 为 1 和 2 的数据进行去重,他使用的是内部临时表,MySQL 在内部创建了一个名为 <union1,2> 的临时表。id 为 null 是表示这个表是临时的。

select_type 列

在前面的例子中我们也说到了一个查询语句中可能包含若干个 SELECT,查询若干个表。每一个表都是一个小查询,而 select_type 就是来说明这个小查询的类型的。

这一部分的笔记,我前期已经整理过了。如果大家感兴趣可以直接点击下面的连接去查看,我这里就不再赘述了。

详细解释MySQL explain 中的 select_type 是什么

table 列

无论我们的查询语句有多复杂,其中包含多少个表,使用什么连接、子查询、UNION 子句等方式进行组合,到最后还是对每个表进行单表访问。

EXPLAIN 语句的输出的每条记录都对应着某个单表的访问方法,该记录的 table 列代表该表的表名。当然这些表不是必须得是数据库中实际存储的表,也有可能是为了方便去重等原因 MySQL 自己搞的临时表,比如说之前 UNION 的情况。

image.png

type 列

坦白的说,这一列应该是我们在查看 MySQL 的执行计划的时候最常看也是最无脑看的一列了。因为我们在学习阿里巴巴的《Java 开发规范》的时候,上面明确规定了 SQL 的级别,原文如下:

image.png

开发规范中的这个所谓的级别,本质上就是说的 EXPLAIN 执行计划中 type 列的级别。完整的访问方法包括 system、const、eq_ref、ref、fulltext、ref_or_null、index_merge、unique_subquery、index_subquery、range、index、ALL。为了方便大家阅读,我先单独整理一个表格来说明各个级别的具体含义,然后再分别具体说明级别信息。

类型描述备注
system如果表里面只有一条数据,而且表使用的存储引擎(如 MyISAM)的统计信息是准确的。条件太苛刻,几乎见不到
const使用主键或唯一二级索引与常数进行等值匹配时效率高,常见
eq_ref连接查询是,如果被驱动表是通过主键或者不允许为 null 的唯一二级索引列进行等值匹配的方法访问的常见
ref通过普通的二级索引与常数进行等值匹配时常见
fulltext全文索引几乎用不到
ref_or_null对二级索引进行等值匹配且该索引的值也可以为 null 的时候常见
index_merge两个以上的索引合并不常见
unique_subquery如果子查询可以转换为 EXISTS 子查询,而且转换之后可以使用主键或者不允许为 null 的唯一二级索引进行等值匹配
index_subquery如果子查询可以转换为 EXISTS 子查询,而且转换之后可以使用普通二级索引进行等值匹配
range使用索引进行范围查询常见
index使用索引覆盖,扫描前部索引记录的时候常见,但是不推荐
ALL全表扫描常见,但是不推荐

事实上,如果只是方便了解这一列的几种级别的话,我觉得上面这个表格就够用了,但是如果你希望针对每每一种级别都想要有更加深入的了解,可以点击查看我之前发布的文章:

详细解释 Type 列

possible_keys 列 和 key 列

在EXPLAIN 输出的执行计划中,possible_keys 列表示在某个查询语句中,对某表执行单标查询时可能用到的索引有哪些,而 key 则是则是表示,最终用到的索引是什么。举个例子:

image.png

但是需要注意的是,并不是可以供选的索引越多越好,因为可以供选择的索引越多,查询优化器在计算查询成本的时候花费的时间就越长。

MySQL 的查询优化器的选择机制因为牵扯到成本计算,如果要说明的话占用的篇幅会非常长,我这里就暂时不展开描述了。我后面会单独整理一篇文章来说明 MySQL 的成本计算逻辑,敬请期待。

ref 列

当我们的查询方法的类别是 const、eq_ref、ref、ref_or_null、unique_subquery、index_subquery 中的其中一个时,ref 列展示的就是与索引列进行等值查询的东西是啥。

它有时是一个常数,有时是一个列,甚至可以是一个 function。这个其实没啥技术含量,我猜测大家看图就能搞明白,所以这里就不赘述了。

image.png

rows 列

如果查询优化器决定使用全表扫描的方式对某个表执行查询时,执行计划的rows列就代表预计需要扫描的行数,如果使用索引来执行查询时,执行计划的rows列就代表预计扫描的索引记录行数。

image.png

filtered 列

filtered 列表示针对预估的需要读取的记录,经过搜索条件过滤后剩余记录条数的百分比。这个感觉说起来不好理解,我们可以举两个例子:

image.png

在这个表中,我们进行全表扫描,显示我们要扫描 57736 行数据。但是通过我加上 where 条件之后,现在预计需要扫描的行数就是 57736 x 10% = 5773 行了。

Extra

Extra 是用来说明一些额外的信息的,其实根据上面的内容,我们大概知道了一个 SQL 的执行计划输出列都是什么,分别代表什么意思。但是通过这列中的说明信息,我们可以更准确的理解 MySQL 到底如何执行给定的查询语句。

Extra 列中可以给出的声明信息非常非常的多,但是我在这里还是做了一些精简,因为这里面实际上有很多我们日常工作中根本用不上(或者很少见到)。我们只要对下面的这些描述有印象就可以了,如果遇到陌生的,可以面向 Google 编程。

  • No tables used: 查询语句中没有 FROM 子句
  • Impossible WHERE 查询语句的 WHERE 子句条件永远为 false。 如: WHERE 1 != 1
  • No matching min/max row :查询列表处有 MIN 或者 MAX 聚集函数,但是没有记录符合 WHERE 子句中的搜索条件
  • Using index : 使用了覆盖索引。
  • Using index condition : 搜索条件中虽然出现了索引类,但是却不能充当边界条件来形成扫描区间。比如 key1 > 'z' and key1 like '%a'
  • Using where: 当某个搜索条件需要在 server 层进行判断时,提示 Using where
  • Using join buffer:连接查询的执行过程中,当被驱动表不能有效的利用索引加快访问速度的时候,使用 join buffer 缓冲区来加快查询速度的时候,会提示这个。
  • Using filesort:在有些情况下对结果集中的记录进行排序的时候,是可以使用到索引的。
  • Using temporary: 在许多查询的执行过程中,借助临时表进行去重、排序等。

Fenix
1 声望0 粉丝