mysql原理之执行流程深度解析
在一年前写过一篇关于mysql的执行流程和日志,现在再来回顾一下。
想看 Mysql原理-执行流程-binLog、redoLog、undoLog的作用-事务的四大特性请移驾至: https://blog.csdn.net/nxw_tsp/article/details/108307772
sql的执行流程
一条sql从你按下执行键那一刻开始到底发生了什么,你就能得到查询的结果呢?
通过上面这张图可以看出,sql执行其实并不是那么简单的就把返回结果给我们了,而是经过了很多复杂的步骤之后才把相应的结果返回给我们。
那么图中所说的query_cache、解析器、预处理、优化器、执行计划、执行器到底是什么呢?下面我来解释以下。
sql执行流程解释
- 从你点击执行sql的那一刻开始,Navicat需要对数据库进行一次连接请求。
- 请求成功后就会对query_cache缓存进行一次查询,看你当前sql的结果是否在缓存里面,如果有则直接返回数据。
- 没有缓存则会到Paser解析器里进行词法解析和语法解析并生成解析树。
- 解析完成后会来到预处理进行权限验证和对解析树进行语义解析,之后会把sql交给优化器进行优化。
- 优化器优化完成之后会交给执行计划来生成执行计划。
- 最后执行器负责按照执行计划拆解好的步骤去存储引擎里执行。
- 最后将结果返回客户端。
什么是query_cache、解析器、预处理、优化器、执行计划、执行器
query_cache(缓存)
官方文档地址:https://dev.mysql.com/doc/refman/5.7/en/query-cache-configuration.html
通过mysql官方文档可以看到在5.7.20版本之后query_cache是默认关闭的,甚至在5.8版本中直接把缓存给移除了。
当然,缓存的开启与否我们也是可以通过命令来查看的
show variables like 'query_cache%'
通过以上命令我们可以看到query_cache_type的值是OFF,说明是已经关闭了的,至于mysql为什么在5.7版本之后是关闭缓存的甚至于在后续的版本把缓存去掉,实际上是因为,在实际开发过程中我们对于数据的实时性的要求是很高的,如果当你第一次查询数据时直接把查询结果缓存下来之后,别的sql对你查询到的数据有修改的话,当你再一次执行该sql就直接从缓存里取到了(而结果仍然是未修改的),而不是从库里查询最新的数据结果。所以就造成了查询到的结果和真实数据不一致的情况。再一在实际的生产环境中,缓存当然要用专业的缓存工具,所以也就导致了后续版本中mysql将缓存剔除的情况。
Parser(解析器)
解析器通常会对我们的sql进行词法解析和语法解析。
词法解析会
词法解析就是把你输入的语句打碎成一个个的单词。
比如你输入一条简单的语句:
select employeeName from employees
词法解析会将sql打碎成四个单词。
语法解析
而当你输入一条规范的查询语句的时候,比如select name from users where id = 1
mysql就会对这条语句进行语法分析并生成解析树,select属于关键词,那么name是字段,from是关键词,users是表,where是关键词 id=1是条件。
解析完毕之后解析树大致如下
语法解析会解析出来关键字和非关键字。
Preprocessor(预处理器)
预处理器会进行权限的认证和语义解析。
权限认证
预处理器会判断你是否有执行select的权限或者insert,update的权限。
语义解析
刚才在解析器说过语法解析会对sql解析后生成一个解析树,但是解析器并不知道你查询的表是否存在,查询的字段是否正确,这个时候就会由预处理来进行验证,来确保你这条sql是可以执行的。
优化器
优化器会对sql进行一个优化,来达到最优。
比如:你users表有一个name和cardId的一个联合索引,
但是你sql写的是select * from users where cardId = 1 and name = ‘Silence’
根据最左匹配原则,这条sql是走不到联合索引的,但是优化器会对sql进行优化,优化为 select * from users where name = ‘Silence’ and cardId = 1
这样,sql执行的时候最终会用到这个联合索引。
执行计划
我们在一条sql前面加上Explain便会看到执行计划
例如我们输入sql
EXPLAIN select * from customer where address_id =(select address_id from address where phone = '745994947458')
执行计划如表中所示
它会告诉执行器,我这有两条语句,你先执行哪一条再执行哪一条。
主要的几个执行计划字段含义
id
执行计划的id id值越大优先级越高,越先被执行,如果id值一样,则从上到下依次执行,
如果sql中有子查询,则子查询的id值会大于最外层的主查询。
select_type
查询语句的类型,主要用来区分普通查询还是联合查询或者说是子查询等。
-
SIMPLE:简单的查询,查询中没有子查询或union的情况
一个简单的查询,他的select_type就是SIMPLE。 -
PRIMARY:主查询,如果有子查询的话最外层的就是PRIMARY
-
SUBQUERY:表示在select或者where包含了子查询
先把where条件的子查询查询出结果后,主查询再查子查询的结果。那么子查询的select_type就是SUBQUERY,最外层主查询的select_type是PRIMARY。 -
UNION:select 后的第二个查询
-
UNION RESULT:union的结果
UNION和UNION RESULT的展示如下: - DERIVED:是在查询FROM子句范围内生成表的表达式。例如,SELECT语句FROM子句中的子查询是派生表
table
查询中用到的表或者别名,中间数据。
type
访问的类型,SQL 查询优化中一个非常重要的指标,结果值从好到坏依次是:system > const > eq_ref > ref > range > index > ALL。
- system:系统表
- const:常量连接
- eq_ref:主键索引(primary key)或非空的唯一索引
- ref:非主键非唯一索引
- range:范围查询
- index:索引查询
- ALL:全表扫描(full table scan)
possible_keys
查询过程中可能用到的索引
key
查询中真实用到的索引
关于执行计划更加详细的描述可以参考:https://www.cnblogs.com/yinjw/p/11864477.html
这篇文章写的非常棒非常细致。
执行器
执行器用来执行执行计划生成的计划,向存储引擎查询数据。