MySQL执行计划理解与实践

SQL执行过程和优化器

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

MySQL执行计划理解与实践_第1张图片

1、客户端发送一条查询给服务器
2、服务器先检查查询缓存,如果命中缓存则立刻返回存储在缓存中的结果,否则进入下一阶段。
3、服务器端进行SQL解析、预处理,再由优化器生成对应的执行计划。
4、MYSQL根据优化器生成的执行计划调用存储引擎的API来执行查询
5、将结果返回给客户端

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

tips:一个sql查询往往有很多种方式,可以弄成子查询,也可以用表连接,这个地方的选择主要就是由优化器来负责。在oracle中,优化器主要有:

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

rbo已经被抛弃不使用了,目前主流使用基于代价的cbo。
基于代价就要计算代价,初始情况下,计算代价的最小单位就是随机读取一个4k页面的成本,并选择其中成本最小的执行计划,后来引入了一些复杂的计算因子来计算某些操作的代价。机器毕竟是机器,很多情况下,cbo优化器也会发生优化不准确的情况,所以这也是为什么需要了解执行过程的原因。

很多情况下都会导致优化器选择了错误的执行计划,并不能获得最好的性能:

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

查看执行计划

    explain select xxx from xxx; 

MySQL执行计划理解与实践_第2张图片

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

  • id :id主要是用来标识sql执行顺序,如果没有子查询,一般来说id只有1个,执行顺序也是从上到下。
  • select_type:每个select子句的类型,主要分成下面几种:
    a:SIMPLE:查询中不包含任何子查询或者union
    b:PRIMARY:查询中包含了任何复杂的子部分,最外层的就会变成PRIMARY
    c:SUBQUERY:在SELECT或者WHERE列表中包含了子查询
    d:DERIVED:在FROM中包含了子查询
    e:UNION:如果第二个SELECT出现在UNION之后,则被标记为UNION,如果UNION包含在FROM子句的子查询中,外层SELECT会被标记为:DERIVED
    f:UNION RESULT从UNION表获取结果的select

type:是指MySQL在表中找到所需行的方式,也就是访问行的”类型”,从a开始,效率逐渐上升:
a:all:全表扫描,效率最低
b:index:index会根据索引树遍历
c:range:索引范围扫描,返回匹配值域的行。
d:ref:非唯一性索引扫描,返回匹配某个单独值的所有行。一般是指多列的唯一索引中的某一列。
e:eq_ref:唯一性索引扫描,表中只有一条记录与之匹配。
f:const、system:主要针对查询中有常量的情况,如果结果只有一行会变成system
g:NULL:显而易见,既不走表,也不走索引

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

key
这个字段表示了mysql真实使用的索引。如果mysql优化过程中没有加索引,可以强制加hint使用索引

key_len
索引长度字段顾名思义,表示了mysql使用的索引的长度.

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

rows
预估结果集的条数,可能不一定完全准确

实践测试

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

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

这是一个两个表连接的查询。

执行计划如下:

MySQL执行计划理解与实践_第3张图片
一个一个来,首先在id相同的情况下,从上往下执行。这是一个右连接查询,以右边为基准,右边的每一行去匹配左边的表,所以首先右边进行s表全表扫描,typeALL,没有走索引。同时两次查询都是简单查询,没有子查询,所以select_type都是simple。

接着是对e表进行查询,typeeq_ref,因为emp_no是主键,唯一性扫描,possible_keyskey都是primary,每次都针对前面的记录扫出对应的一行。

你可能感兴趣的:(Mysql)