执行计划详解

时间:2022-04-05 16:47:03
一、ID列
1.单纯的join(没有子查询) ID都是1,且从上到下。
 
desc select count(*) from employees d
join salaries t on d.emp_no=t.emp_no
join employees s on d.emp_no=s.emp_no
where salary between 600 and 66961;
  执行计划详解

 

 
执行计划详解
 
上述SQL的执行顺序为:首先运行d,再运行s,最后运行t。
MySQL看执行计划都是从上往下看。
 
2.subquery(子查询),scala subquery(标量子查询)都会使ID递增。
 
desc select count(*),(select count(1) from t_group t2 where t2.emp_no=s.emp_no limit 1) b
,(select count(1) from t_group t2 where t2.emp_no=s.emp_no limit 1) c
from employees d
join salaries t on d.emp_no=t.emp_no
join employees s on d.emp_no=s.emp_no
where t.salary between 6000 and 66961;
 
执行计划详解
  执行计划详解

 

 
 
二、select_type 列
 
 
1.simple
不需要union 操作或者没有子查询的都是simple。
最外层SELECT查询的select_type通常为SIMPLE。
 
2.primary
用union 操作或者有子查询的都是primary 。
 
desc select count(*),(select count(1) from t_group t2 where t2.emp_no=s.emp_no limit 1) b
,(select count(1) from t_group t2 where t2.emp_no=s.emp_no limit 1) c
from (select * from employees limit 100) s;
执行计划详解
 
 
3.UNION
由UNION操作联合而成的单位SELECT查询中,除第一个外,第二个以后的所有单位SELECT查询的select_type都是是UNION。
UNION的第一个单位SELECT的select_type(下面的UNION RESULT)不是UNION,而是DERIVED,它是一个去掉重复值的临时表,用于存储UNION后的查询结果。
 
desc select * from dept_emp a where dept_no='d003'
union
select * from dept_emp b where dept_no='d004'
union
select * from dept_emp c where dept_no='d005';
  执行计划详解

 

 
执行计划详解
 
desc select * from dept_emp a where dept_no='d003'
union all
select * from dept_emp b where dept_no='d004'
union all
select * from dept_emp c where dept_no='d005';
  执行计划详解

 

执行计划详解
 
union all不出现union result是因为不去重。
 
4.subquery
这里的subquery仅仅指除from子句以外使用的子查询。注意点是跟外部表没有关联。
 
desc select count(*) from employees d
join salaries t on d.emp_no=t.emp_no
join employees s on d.emp_no=s.emp_no
where salary between 6000 and 66961 ;
  执行计划详解

 

执行计划详解
 
执行计划没有错误,不代表SQL执行没错误。
 
desc
select count(1)
,(select d.emp_no from employees d
join salaries t on d.emp_no=t.emp_no
where salary between 6000 and 66961 ) a
from t_group t limit 1
ERROR 1242 (21000): Subquery returns more than 1 row
 
5.dependent subquery
必须依附于外面的值,如scala subquery或者exists。
由于dependent subquery先执行外部查询,再执行内部查询(子查询),所以它比一般子查询(不带dependent 关键字)的处理速度要慢。
 
5.1. scala subquery
desc select count(*) ,(select count(1) from t_group t2 where t2.emp_no=s.emp_no limit 1) b
,(select count(1) from t_group t2 where t2.emp_no=s.emp_no limit 1) c
from employees s;
执行计划详解
  执行计划详解

 

5.2 exists
desc select count(*)
from employees s
where exists (select count(1) from t_group t2 where t2.emp_no=s.emp_no limit 1)
执行计划详解
  执行计划详解

 

6.derived
(1)from后面表的位置上的subquery。
(2)derived是生成在内存或者临时表空间中的。
(3)如果derived当驱动表的时候,要点是要减少数据量为目的。
(4)当作被驱动表的时候,产生auto_key,也是要减少数据量为目的。
(5)5.7当中,optimizer_swith='derived_merge=on'可以把简单subquery打开成join。
 
7.materialized
 
 
三、table列
1.null表示不使用任何表。
2.表名或者有别名。
3.<derved + id> <union + id> 表示是临时表<> 里的数字是id列。
 
 
 
四、Type列
1.const
使用primary key或者uniquer取得一条数据。
特点是 show warnings\G可以显示常数。
这说明了MySQL执行计划是事先运行一部分。
 
desc select * from employees where emp_no=10001;
执行计划详解
  执行计划详解

 

 
2.eq_ref
(1)必须是join。
(2)且满足被驱动表的连接条件uniquer key或者primary key。
(3)如果是复合主键或者unique key相应的列名必须都要包含。
 
desc select * from dept_emp d join employees e on d.emp_no=e.emp_no where d.dept_no='d005';
执行计划详解
 
  执行计划详解

 

 
3.ref
对索引列,进行 = (“等于”)条件。
 
desc select * from dept_emp d where d.dept_no='d005';
执行计划详解
  执行计划详解

 

4.range
索引范围查询,主要出现在 >,< ,between,in,like等。
 
desc select * from employees d where d.emp_no>10001;
执行计划详解
  执行计划详解

 

 
5.index
索引全扫描,比起表全扫描且order by的情况下快。
但是绝大部分情况下是优化对象。
主要用于:
(1)不能使用range、const、ref。
(2)只查询索引列,即不回表。
(3)使用索引进行排序或者聚合即省略排序。
 
满足联合索引中前导列没有在where条件且查询列在索引或者primary key中包含emp_no
desc select emp_no from salaries where from_date='1986-06-26';
执行计划详解
  执行计划详解

 

在聚合运算中,group by后面的列在索引或者primary key中,且查询列也是在索引中。
desc select from_date,count(*) from salaries group by from_date;
 

 执行计划详解

 

执行计划详解
 
desc select emp_no,from_date,count(*) from salaries group by emp_no,from_date;
执行计划详解
  执行计划详解

 

 
6.all
全表扫描。
一般情况下是非常不好的执行计划,但是在进行大表中查询超过一半以上的值的时候效果会更好。
导致全表扫描的情况较多:没有索引,对索引进行加工,索引列进行了隐藏类型转换,对日期类型进行like 20%,单列索引的时候,对数字列进行 like 30%。
 
单列索引,对索引列进行加工。
desc select * from employees where emp_no +1=10001;
执行计划详解
  执行计划详解

 

索引列进行了隐藏类型转换
desc select * from test1 where id =10;
执行计划详解
 
执行计划详解

 

对日期类型进行like 20%
执行计划详解
 
  执行计划详解

 

执行计划详解
 
 
对数字列进行like 30%
desc select * from employees where emp_no like '10001%'
 
 
 
五、possible_keys
列出表所在的索引名称,对优化没有什么帮助,请无视他。
 
 
六、key
实际使用的索引名称。
key中列出索引名称。
show index from tab中查询此索引都有哪些列。
 
执行计划详解
  执行计划详解

 

 
 
七、key_len
在复合索引中可以看出索引中到底使用了哪些索引。
下面的例子中可以看出key_len是不一样的。
desc select * from salaries where emp_no='10001' and from_date like '1986%';
 
执行计划详解
  执行计划详解

 

desc select * from salaries where emp_no='10001' and from_date='1986-06-26';
执行计划详解
第一个例子like '1986%' 中说明 from_date的索引没有用到,只用到了emp_no,所以是4 。第二个例子 ='1986-06-26',说明用到了emp_no和from_date索引,所以是4+3=7 。
  执行计划详解

 

 

八、Rows
此列是MySQL优化器根据统计信息预估出来的值,不准确。
执行计划详解
  执行计划详解

 

执行计划详解
  执行计划详解

 

 
九、filtered
跟rows一样是预估值,ritered非100的情况是extra有using where关键字,表示从innodb引擎中拿到数据后再加工的比率。
执行计划详解
  执行计划详解

 

 
十、Extra
 
1.distinct
MySQL在join过程中d中取出一行之后,查询de表的时候,碰到一行就停止,有点像exists。
(1)条件时必须是join。
(2)distinct的是驱动表的主键且只能是单列。
(3)被驱动表必须不能回表,且跟驱动表是1:M关系。
 
执行计划详解
  执行计划详解

 

 
 
2.select tables optimized away
复合主键(两个)的时候,使用其中的任何一个只能用 = 号,求出另一个且不能保护group by就是在计算一个索引的上下两列一行。
执行计划详解
 
  执行计划详解

 

执行计划详解
  执行计划详解

 

 
 
select当中只有min,max,count的时候出现。
执行计划详解
 
  执行计划详解

 

 
3.using filesort
在进行order by,group by且没有使用索引的时候。
执行计划详解
 
执行计划详解
  执行计划详解

 

 
4.using index
只使用索引,不回表就可以查到。所有的数据都在数据中就可以取出来。
执行计划详解
 
执行计划详解
  执行计划详解

 

  执行计划详解

 

 
5.using temporary
MySQL执行过程中为了存储中间结果会使用 temporary table。
如果执行计划中出现using temporary即使使用 temporary table,但是还是无法判断实在内存中生成还是在disk中生成。
(1)order by,group by没有使用索引的时候。
执行计划详解
  执行计划详解

 

执行计划详解

 

执行计划详解
 
 
(2)执行计划中的select_type为derved。
执行计划详解
 
  执行计划详解

 

执行计划详解
 
  执行计划详解

 

  执行计划详解

 

执行计划详解
执行计划详解
 
 
6.using where
一般using where跟filtered ,rows一起看。
using where 表示从存储引擎中拿到一些数据,然后再过滤。
其中rows是在存储引擎中拿数据预算值,filtered是再过滤的百分百。
执行计划详解
  执行计划详解

 

 
 
下面的rows 表示key primary const模式中有17行数据从innodb中导入到MySQL Server内存中,其中在内存中过滤掉了11%数据。简单来说就是emp_no=‘10001’ 数据通过primary 索引。把17行数据倒到内存中然后通过from_date like ‘1986%’ fiter之后最终17*11%的数据给用户。
  执行计划详解

 

执行计划详解
 
几个例子:
Eg:1
 
desc select t1.emp_no from t1 join (
select distinct a.emp_no from (select e.emp_no from employees e where e.emp_no between 10001 and 11000 limit 1000) a
) E1 on t1.emp_no = E1.emp_no limit 5 ;
 
执行计划详解
 
  执行计划详解