2020
2121本篇文章会分析下一个 sql 语句在 MySQL 中的执行流程,包括 sql 的查询在 MySQL 内部会怎么流转,sql 语句的更新是怎么完成的。
2222
23- 在分析之前我会先带着你看看 MySQL 的基础架构,知道了 MySQL 由那些组件组成已经这些组件的作用是什么 ,可以帮助我们理解和解决这些问题。
23+ 在分析之前我会先带着你看看 MySQL 的基础架构,知道了 MySQL 由那些组件组成以及这些组件的作用是什么 ,可以帮助我们理解和解决这些问题。
2424
2525## 一 MySQL 基础架构分析
2626
3030
3131先简单介绍一下下图涉及的一些组件的基本作用帮助大家理解这幅图,在 1.2 节中会详细介绍到这些组件的作用。
3232
33- - ** 连接器:** 身份认证和权限相关(登录 MySQL 的时候)。
34- - ** 查询缓存: ** 执行查询语句的时候,会先查询缓存(MySQL 8.0 版本后移除,因为这个功能不太实用)。
35- - ** 分析器: ** 没有命中缓存的话,SQL 语句就会经过分析器,分析器说白了就是要先看你的 SQL 语句要干嘛,再检查你的 SQL 语句语法是否正确。
36- - ** 优化器:** 按照 MySQL 认为最优的方案去执行。
37- - ** 执行器: ** 执行语句,然后从存储引擎返回数据。
33+ - ** 连接器:** 身份认证和权限相关(登录 MySQL 的时候)。
34+ - ** 查询缓存: ** 执行查询语句的时候,会先查询缓存(MySQL 8.0 版本后移除,因为这个功能不太实用)。
35+ - ** 分析器: ** 没有命中缓存的话,SQL 语句就会经过分析器,分析器说白了就是要先看你的 SQL 语句要干嘛,再检查你的 SQL 语句语法是否正确。
36+ - ** 优化器:** 按照 MySQL 认为最优的方案去执行。
37+ - ** 执行器: ** 执行语句,然后从存储引擎返回数据。
3838
3939![ ] ( https://user-gold-cdn.xitu.io/2019/3/23/169a8bc60a083849?w=950&h=1062&f=jpeg&s=38189 )
4040
@@ -96,7 +96,7 @@ select * from tb_student A where A.age='18' and A.name=' 张三 ';
9696结合上面的说明,我们分析下这个语句的执行流程:
9797
9898* 先检查该语句是否有权限,如果没有权限,直接返回错误信息,如果有权限,在 MySQL8.0 版本以前,会先查询缓存,以这条 sql 语句为 key 在内存中查询是否有结果,如果有直接缓存,如果没有,执行下一步。
99- * 通过分析器进行词法分析,提取 sql 语句的关键元素,比如提取上面这个语句是查询 select,提取需要查询的表名为 tb_student, 需要查询所有的列,查询条件是这个表的 id='1'。然后判断这个 sql 语句是否有语法错误,比如关键词是否正确等等,如果检查没问题就执行下一步。
99+ * 通过分析器进行词法分析,提取 sql 语句的关键元素,比如提取上面这个语句是查询 select,提取需要查询的表名为 tb_student, 需要查询所有的列,查询条件是这个表的 id='1'。然后判断这个 sql 语句是否有语法错误,比如关键词是否正确等等,如果检查没问题就执行下一步。
100100* 接下来就是优化器进行确定执行方案,上面的 sql 语句,可以有两种执行方案:
101101
102102 a.先查询学生表中姓名为“张三”的学生,然后判断是否年龄是 18。
@@ -112,7 +112,7 @@ select * from tb_student A where A.age='18' and A.name=' 张三 ';
112112```
113113update tb_student A set A.age='19' where A.name=' 张三 ';
114114```
115- 我们来给张三修改下年龄,在实际数据库肯定不会设置年龄这个字段的,不然要被技术负责人打的。其实条语句也基本上会沿着上一个查询的流程走 ,只不过执行更新的时候肯定要记录日志啦,这就会引入日志模块了,MySQL 自带的日志模块式 ** binlog(归档日志)** ,所有的存储引擎都可以使用,我们常用的 InnoDB 引擎还自带了一个日志模块 ** redo log(重做日志)** ,我们就以 InnoDB 模式下来探讨这个语句的执行流程。流程如下:
115+ 我们来给张三修改下年龄,在实际数据库肯定不会设置年龄这个字段的,不然要被技术负责人打的。其实这条语句也基本上会沿着上一个查询的流程走 ,只不过执行更新的时候肯定要记录日志啦,这就会引入日志模块了,MySQL 自带的日志模块是 ** binlog(归档日志)** ,所有的存储引擎都可以使用,我们常用的 InnoDB 引擎还自带了一个日志模块 ** redo log(重做日志)** ,我们就以 InnoDB 模式下来探讨这个语句的执行流程。流程如下:
116116
117117* 先查询到张三这一条数据,如果有缓存,也是会用到缓存。
118118* 然后拿到查询的语句,把 age 改为 19,然后调用引擎 API 接口,写入这一行数据,InnoDB 引擎把数据保存在内存中,同时记录 redo log,此时 redo log 进入 prepare 状态,然后告诉执行器,执行完成了,随时可以提交。
@@ -121,7 +121,7 @@ update tb_student A set A.age='19' where A.name=' 张三 ';
121121
122122** 这里肯定有同学会问,为什么要用两个日志模块,用一个日志模块不行吗?**
123123
124- 这是因为最开始 MySQL 并没与 InnoDB 引擎( InnoDB 引擎是其他公司以插件形式插入 MySQL 的) ,MySQL 自带的引擎是 MyISAM,但是我们知道 redo log 是 InnoDB 引擎特有的,其他存储引擎都没有,这就导致会没有 crash-safe 的能力(crash-safe 的能力即使数据库发生异常重启,之前提交的记录都不会丢失),binlog 日志只能用来归档。
124+ 这是因为最开始 MySQL 并没有 InnoDB 引擎( InnoDB 引擎是其他公司以插件形式插入 MySQL 的) ,MySQL 自带的引擎是 MyISAM,但是我们知道 redo log 是 InnoDB 引擎特有的,其他存储引擎都没有,这就导致会没有 crash-safe 的能力(crash-safe 的能力即使数据库发生异常重启,之前提交的记录都不会丢失),binlog 日志只能用来归档。
125125
126126并不是说只用一个日志模块不可以,只是 InnoDB 引擎就是通过 redo log 来支持事务的。那么,又会有同学问,我用两个日志模块,但是不要这么复杂行不行,为什么 redo log 要引入 prepare 预提交状态?这里我们用反证法来说明下为什么要这么做?
127127
@@ -138,10 +138,10 @@ update tb_student A set A.age='19' where A.name=' 张三 ';
138138
139139## 三 总结
140140
141- * MySQL 主要分为 Server 层和引擎层,Server 层主要包括连接器、查询缓存、分析器、优化器、执行器,同时还有一个日志模块(binlog),这个日志模块所有执行引擎都可以共用, redolog 只有 InnoDB 有。
141+ * MySQL 主要分为 Server 层和引擎层,Server 层主要包括连接器、查询缓存、分析器、优化器、执行器,同时还有一个日志模块(binlog),这个日志模块所有执行引擎都可以共用, redolog 只有 InnoDB 有。
142142* 引擎层是插件式的,目前主要包括,MyISAM,InnoDB,Memory 等。
143- * 查询语句的执行流程如下:权限校验(如果命中缓存)---》 查询缓存---》 分析器---》 优化器---》 权限校验---》 执行器---》 引擎
144- * 更新语句执行流程如下:分析器----》 权限校验----》 执行器---》 引擎---redo log(prepare 状态---》 binlog---》 redo log(commit状态)
143+ * 查询语句的执行流程如下:权限校验(如果命中缓存)---> 查询缓存---> 分析器---> 优化器---> 权限校验---> 执行器---> 引擎
144+ * 更新语句执行流程如下:分析器----> 权限校验----> 执行器---> 引擎---redo log(prepare 状态) ---> binlog---> redo log(commit状态)
145145
146146## 四 参考
147147
0 commit comments