每条SQL语句,mysql都会经过成本和规则的优化,对这个SQL选择对应的 一些访问方法和顺序,包括做一些特殊的改写确保执行效率是最优的,然后优化过后,就会得到一个执 行计划。
如果你把之前的内容都学习的比较透彻的话就会知道,所谓的执行计 划,落实到底层,无非就是先访问哪个表,用哪个索引还是全表扫描,拿到数据之后如何去聚簇索引回 表,是否要基于临时磁盘文件做分组聚合或者排序
如果你的SQL语句极为的复杂,可能这里会有很多条数据,因为一个复杂的SQL语句的执行是要拆 分为很多步骤的,比如先访问表A,接着搞一个排序,然后来一个分组聚合,再访问表B,接着搞一个连 接
id
每个SELECT都会对应一个id,其实说穿了,就是一个复杂的SQL里可能会有很多个 SELECT,也可能会包含多条执行计划,每一条执行计划都会有一个唯一的id
select_type、table、partitions
就是这一条执行计划对应的查询是个什么查询类型,table就是表名,意 思是要查询哪个表,partitions是表分区的概念。
type
针对当前这个表的访问方法,const、ref、range、index、all
possible_keys
跟type结合起来,type 确定访问方式了,那么到底哪些索引是可供选择,都会显示在这里。
key,就是在possible_keys 里实际选择的那个索引,而key_len是索引的长度
ref
使用某个字段的索引进行等值匹配搜索的时候,跟索引列进行等值匹配的那个目标值的一些信息
rows
预估通过索引或者别的方式访问这个表的时候,大概可能会读取多少条数据。
filtered
extra
是一些额外的信息,不是太重要
explain select * from t1

- select_type是SIMPLE , 顾名思 义,这个表的查询类型是很普通的、而且简单的就可以了
- all就是全表扫描,这没办法,你完全没加任何where条件,那当然只能是 全表扫描了!即 就会知道,这里直接会扫描表的聚簇索引的叶子节 点,按顺序扫描过去拿到表里全部数据。
- rows是3457,这说明全表扫描会扫描这个表的3457条数据,说明这个表里就有3457条数据,此时你全 表扫描会全部扫描出来。
- filtered是100%,这个也很简单了,你没有任何where过滤条件,所以直接筛 选出来的数据就是表里数据的100%占比。
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,也就是嵌套循环的访问方式。
explain select * from t1 where x1 in(select x1 from t2) or x3 = 'xxxx';
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows |filtered | Extra |
| 1 | PRIMARY | t1 | NULL | ALL | index_x3 | NULL | NULL | NULL | 3457 | 100.00 | Using where |
| 2 | SUBQUERY | t2 | NULL | index | index_x1 | index_x1 | 507 | NULL | 4687 | 100.00 | Using index |
SQL里有两个SELECT,主查询SELECT的执行计划的id就是1,子查询SELECT的执行 计划的id就是2 ;
第一条执行计划里,select_type是PRIMARY,不是SIMPLE了, 说明第一个执行计划的查询类型 是主查询的意思
有一个where条件是x3=’xxx’,所以他的possible_keys里包含了 index_x3,就是x3字段的索引,但是他的key实际是NULL,而且type是ALL,所以说他最后没选择用x3 字段的索引,而是选择了全表扫描;这是为什么呢?其实很简单,可能他通过成本分析发现,使用x3字段的索引扫描xxx这个值,几乎就跟 全表扫描差不多,可能x3这个字段的值几乎都是xxx,所以最后就选择还不如直接全表扫描呢。
接着第二条执行计划,他的select_type是SUBQUERY,也就是子查询,子查询针对的是t2这个表,当然 子查询本身就是一个全表查询,但是对主查询而言,会使用x1 in 这个筛选条件,他这里type是index, 说明使用了扫描index_x1这个x1字段的二级索引的方式,直接扫描x1字段的二级索引,来跟子查询的结 果集做比对。
EXPLAIN SELECT * FROM t1 UNION SELECT * FROM t2
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
| 1 | PRIMARY | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 3457 | 100.00 | NULL |
| 2 | UNION | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 4687 | 100.00 | NULL |
其实union字句默认的作用是把两个结果集合并起来还会进行去重 —即干的去重的活:
| NULL | UNION RESULT |
所以上面他的table是
