透彻研究通过explain命令得到的SQL执行计划(一)

只要把explain分析得到的SQL执行计划都研究透彻,完全能看懂,知道每个执行计划在底层是怎么执行的,那么后面SQL语句的调优就非常容易了。

首先,现在应该都知道每条SQL语句,mysql都会经过成本和规则的优化,对这个SQL选择对应的一些访问方法和顺序,包括做一些特殊的改写确保执行效率是最优的,然后优化过后,就会得到一个执行计划。

这个执行计划其实真没那么神秘,如果把之前的内容都学习的比较透彻的话就会知道,所谓的执行计划,落实到底层,无非就是先访问哪个表,用哪个索引还是全表扫描,拿到数据之后如何去聚簇索引回表,是否要基于临时磁盘文件做分组聚合或者排序,其实这个计划到最后就是这点东西。

平时只要用类似于:explain select * from table,这种SQL前面加一个explain命令,就可以轻松拿到这个SQL语句的执行计划,现在就来看看,这个所谓的执行计划里会有哪些东西。

首先,当执行explain命令之后,拿到的执行计划可能是类似下面这样的东西:

id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra          
 
|+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+|  
 
1 | SIMPLE      | NULL  | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL |     NULL | No tables used |

大致来说,如果是一个简单的单表查询,可能这里就只有一条数据,也就是代表了它是打算如何访问这一个表而已。

但是如果SQL语句极为的复杂,可能这里会有很多条数据,因为一个复杂的SQL语句的执行是要拆分为很多步骤的,比如先访问表A,接着搞一个排序,然后来一个分组聚合,再访问表B,接着搞一个连接,类似这样子。

首先是id这个东西,就是说每个SELECT都会对应一个id,其实说穿了,就是一个复杂的SQL里可能会有很多个SELECT,也可能会包含多条执行计划,每一条执行计划都会有一个唯一的id,这个没啥好说的。

select_type,顾名思义,说的就是这一条执行计划对应的查询是个什么查询类型,table就是表名,意思是要查询哪个表,partitions是表分区的概念。

type,就是比较关键了,针对当前这个表的访问方法,这个之前都讲过很多,比如说const、ref、range、index、all之类的,分别代表了使用聚簇索引、二级索引、全表扫描之类的访问方式。

possible_keys,这也很关键,它是跟type结合起来的,意思就是说你type确定访问方式了,那么到底有哪些索引是可供选择,可以使用的呢,这都会放这里。key,就是在possible_keys里实际选择的那个索引,而key_len就是索引的长度。

ref,就是使用某个字段的索引进行等值匹配搜索的时候,跟索引列进行等值匹配的那个目标值的一些信息。rows,是预估通过索引或者别的方式访问这个表的时候,大概可能会读取多少条数据。filtered,就是经过搜索条件过滤之后的剩余数据的百分比。extra是一些额外的信息,不是太重要。

不同的SQL语句的执行计划长什么样子,先来看第一条SQL语句,特别的简单,就是:

explain select * from t1

就这么一个简单的SQL语句,那么假设它这个里面有大概几千条数据,此时执行计划看起来是什么样的?

+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
 
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
 
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
 
|  1 | SIMPLE      | t1    | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 3457 |   100.00 | NULL  |
 
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+

首先呢,id是1,这个不用管他了,select_type是SIMPLE,这个先不说它什么意思,要知道顾名思义,这个表的查询类型是很普通的、而且简单的就可以了。

table是t1,这还用说么?表名就是t1,所以意思就是这里要访问t1这个表。type是all,这就是我们之前提到的多种访问方式之一了,all就是全表扫描,这没办法,你完全没加任何where条件,那当然只能是全表扫描了!而且如果还记得之前讲解的底层访问方式,就会知道,这里直接会扫描表的聚簇索引的叶子节点,按顺序扫描过去拿到表里全部数据。

rows是3457,这说明全表扫描会扫描这个表的3457条数据,说明这个表里就有3457条数据,此时全表扫描会全部扫描出来。filtered是100%,这个也很简单了,没有任何where过滤条件,所以直接筛选出来的数据就是表里数据的100%占比。

接着再来看一个SQL语句的执行计划:

explain select * from t1 join t2

这是一个典型的多表关联语句,之前说过,这种关联语句,实际上会选择一个表先查询出来数据,接着遍历每一条数据去另外一个表里查询可以关联在一起的数据,然后关联起来,此时执行计划大概长下面这样子:

+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------------+
 
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
 
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------------+
 
|  1 | SIMPLE      | t1    | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 3457 |   100.00 | NULL |
 
|  1 | SIMPLE      | t2    | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 4568 |   100.00 | Using join buffer (Block Nested Loop) |
 
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------------+

这个执行计划就比较有意思了,因为是一个多表关联的执行计划。首先很明显,它的执行计划分为了两条,也就是会访问两个表,先看是如何访问第一个表的,针对第一个表就是t1,明显是先用ALL方式全表扫描它了,而且扫出了3457条数据。

接着对第二个表的访问,也就是t2表,同样是全表扫描,因为它这种多表关联方式,基本上是笛卡尔积的效果,t1表的每条数据都会去t2表全表扫描所有4568条数据,跟t2表的每一条数据都会做一个关联,而且extra里说了是Nested Loop,也就是嵌套循环的访问方式,跟之前讲解的关联语句的执行原理都是匹配的。

另外会发现上面两条执行计划的id都是1,是一样的,实际上一般来说,在执行计划里,一个SELECT会对应一个id,因为这两条执行计划对应的是一个SELECT语句,所以它们俩的id都是1,是一样。

如果要是有一个子查询,有另外一个SELECT,那么另外一个SELECT子查询对应的执行计划的id就可能是2了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值