MySQL Explain

explain是对MySQL的select, update, insert的这三种操做执行计划的描述,可根据其反馈的信息作MySQL的优化。mysql

SQL执行过程和优化器

首先看一下MySQL中,一条sql的执行过程,这里主要是引用了《高性能MySQL》中的内容:算法

一、客户端发送一条查询给服务器 ;
二、服务器先检查查询缓存,若是命中缓存则马上返回存储在缓存中的结果,不然进入下一阶段;
三、服务器端进行SQL解析、预处理,再由优化器生成对应的执行计划;
四、MYSQL根据优化器生成的执行计划调用存储引擎的API来执行查询 ;
五、将结果返回给客户端;sql

对于mysql来讲,底层的存储引擎主要的工做就是对数据的磁盘文件进行操做,上层的sql解析等操做主要放在服务器层,服务器层根据sql进行优化和解析生成执行计划,最后存储引擎根据执行计划来调用数据,有机会必定要读一下这个源码,体会一下这个过程。数据库

tips:一个sql查询每每有不少种方式,能够弄成子查询,也能够用表链接,这个地方的选择主要就是由优化器来负责。缓存

在oracle中,优化器主要有:bash

rbo:基于规则的优化器 
cbo:基于代价的优化器
服务器

rbo已经被抛弃不使用了,目前主流使用基于代价的cbo。 
基于代价就要计算代价,初始状况下,计算代价的最小单位就是随机读取一个4k页面的成本,并选择其中成本最小的执行计划,后来引入了一些复杂的计算因子来计算某些操做的代价。机器毕竟是机器,不少状况下,cbo优化器也会发生优化不许确的状况,因此这也是为何须要了解执行过程的缘由。mysql优化

 

不少状况下都会致使优化器选择了错误的执行计划,并不能得到最好的性能:并发

统计信息不许确,mysql依赖存储引擎提供的统计信息来评估成本,可是有的存储引擎提供的信息是准确的,有的不许确
执行计划中的成本估算不等同于实际执行的成本,由于MYSQL层面并不知道那些页面在内存和磁盘上因此到底须要多少次物理IO未知。
MySQL的最优化和你的最优化并不同(目标不一样,可能你但愿最少时间,MySQL优化目标是最大吞吐量)
MYSQL不考虑并发查询
MYSQL并非任什么时候候都基于成本优化
MYSQL不会考虑不受其控制的操做的成本oracle

查看执行计划

explain select xxx from xxx; 


上面这是最简单的执行计划实例,来分析一下上面的这几个字段。

id: 主要是用来标识sql执行顺序,若是没有子查询,通常来讲id只有1个,执行顺序也是从上到下。


select_type: 每一个select子句的类型,主要分红下面几种: 
1: SIMPLE: 查询中不包含任何子查询或者union 
2: PRIMARY: 查询中包含了任何复杂的子部分,最外层的就会变成PRIMARY
3: SUBQUERY: 在SELECT或者WHERE列表中包含了子查询 
4: DERIVED: 在FROM中包含了子查询 
5: UNION: 若是第二个SELECT出如今UNION以后,则被标记为UNION,
                     若是UNION包含在FROM子句的子查询中,外层SELECT会被标记为:DERIVED 
6: UNION RESULT: 从UNION表获取结果的select

 

table: 查询的表


type: 是指MySQL在表中找到所需行的方式,也就是访问行的“类型”,从1开始,效率逐渐上升: 
1: all: 全表扫描,效率最低 
2: index: index会根据索引树遍历 
3: range: 索引范围扫描,返回匹配值域的行。 
4: ref: 非惟一性索引扫描,返回匹配某个单独值的全部行。通常是指多列的惟一索引中的某一列。 
5: eq_ref: 惟一性索引扫描,表中只有一条记录与之匹配。 
6: const, system: 主要针对查询中有常量的状况,如将主键置于where列表中,MySQL就能将该查询转换为一个常量。若是结果只有一行会变成system 
7: NULL: 显而易见,既不走表,也不走索引

possible_keys 
possible_keys列预估了mysql可以为当前查询选择的索引,这个字段是彻底独立于执行计划中输出的表的顺序,意味着在实际查询中可能用不到这些索引。 若是该字段为空则意味着没有可以使用的索引,这个时候你能够考虑为where后面的字段创建索引

key 
这个字段表示了mysql真实使用的索引。若是mysql优化过程当中没有加索引,键是NULL。要想强制MySQL使用或忽视possible_keys列中的索引,在查询中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。

key_len 
索引长度字段顾名思义,表示了mysql使用的索引的长度。在不损失精确性的状况下,长度越短越好。

ref 
这个字段通常是指一些常量用于选择过滤

rows 
这个字段是MySQL估计的 为了找到所需的行而要读取的行数。这个数字是内嵌循环关联计划里的循环数目,也就是说它不是MySQL认为它最终要从表里读取出来的行数,而是MySQL为了找到符合查询的每一点上标准的那些行而必须读取的行的平均数。

它提供了试图分析全部存在于累计结果集中的行数目的MySQL优化器估计值。QEP 很容易描述这个很困难的统计量。

查询中总的读操做数量是基于合并以前行的每一行的rows 值的连续积累而得出的。这是一种嵌套行算法

filtered (5.7版本以后默认会显示的,在此以前要使用explain extended 才会显示)

它显示的是 返回结果的行数 占 rows字段数值 的 百分比 所作的一个 悲观估算值。比值越大,代表执行计划越准确。

Extra

关于MYSQL如何解析查询的额外信息。将在表4.3中讨论,但这里能够看到的坏的例子是Using temporary和Using filesort,意思MYSQL根本不能使用索引,结果是检索会很慢。如下是一些具体的例子: 

1. Distinct :一旦mysql找到了与行相联合匹配的行,就再也不搜索了。

2. Not exists :mysql优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的行,就再也不搜索了。

3. Range checked for each Record(index map:#) :没有找到理想的索引,所以对从前面表中来的每个行组合,mysql检查使用哪一个索引,并用它来从表中返回行。这是使用索引的最慢的链接之一。

4. Using filesort :MySQL中没法利用索引完成的排序操做称为“文件排序”。看到这个的时候,查询就须要优化了。mysql须要进行额外的步骤来发现如何对返回的行排序。它根据链接类型以及存储排序键值和匹配条件的所有行的行指针来排序所有行。

5. Using index :列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的所有的请求列都是同一个索引的部分的时候。

6. Using temporary :看到这个的时候,查询须要优化了。这里,mysql须要建立一个临时表来存储结果,这一般发生在对不一样的列集进行ORDER BY上,而不是GROUP BY上。(常见于排序和分组查询?)

7. Where used :使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。若是不想返回表中的所有行,而且链接类型ALL或index,这就会发生,或者是查询有问题。

 

总结:
• EXPLAIN不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响状况
• EXPLAIN不考虑各类Cache
• EXPLAIN不能显示MySQL在执行查询时所做的优化工做
• 部分统计信息是估算的,并不是精确值
• EXPALIN只能解释SELECT操做,其余操做要重写为SELECT后查看执行计划。(错误X,EXPLAIN对增删改查操做均可以查看执行计划)

 

实践测试

这里取数据库中最经典的employees库做为例子,两张表,一张employees雇员表,一张salaries薪水表:

`explain select e.birth_date,s.salary from employees e right join salaries s on e.emp_no = s.emp_no;`

这是一条 两表链接 的查询。

执行计划以下:

 
一个一个来,首先在id相同的状况下,从上往下执行。这是一个右链接查询,以右边为基准,右边的每一行去匹配左边的表,因此首先右边进行s表全表扫描, type为ALL,没有走索引。同时两次查询都是简单查询,没有子查询,因此select_type都是simple。

接着是对e表进行查询,type为eq_ref,由于emp_no是主键,惟一性扫描,possible_keys和key都是primary,每次都针对前面的记录扫出对应的一行。

相关文章
相关标签/搜索