1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-06-16 18:10:13 +08:00

Merge pull request #245 from kinglaw1204/master

Add:一条sql语句在mysql中如何执行的
This commit is contained in:
SnailClimb 2019-03-29 16:43:26 +08:00 committed by GitHub
commit c1af12b819
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -0,0 +1,112 @@
![](https://user-gold-cdn.xitu.io/2019/3/21/1699dea772f58967?w=800&h=531&f=jpeg&s=58821)
## 概览
最近开始在学习mysql相关知识自己根据学到的知识点根据自己的理解整理分享出来本篇文章会分析下一个sql语句在mysql中的执行流程包括sql的查询在mysql内部会怎么流转sql语句的更新是怎么完成的。
## 一、mysql架构分析
下面是mysql的一个简要架构图
![](https://user-gold-cdn.xitu.io/2019/3/23/169a8bc60a083849?w=950&h=1062&f=jpeg&s=38189)
mysql主要分为Server层和存储引擎层
**Server层**:主要包括连接器、查询缓存、分析器、优化器、执行器等,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图,函数等,还有一个通用的日志模块 binglog日志模块。
**存储引擎** 主要负责数据的存储和读取采用可以替换的插件式架构支持InnoDB、MyISAM、Memory等多个存储引擎其中InnoDB引擎有自有的日志模块redolog 模块。
InnoDB 5.5.5版本作为默认引擎。
**连接器**
主要负责用户登录数据库,进行用户的身份认证,包括校验账户密码,权限等操作,如果用户账户密码已通过,连接器会到权限表中查询该用户的所有权限,之后在这个连接里的权限逻辑判断都是会依赖此时读取到的权限数据,也就是说,后续只要这个连接不断开,即时管理员修改了该用户的权限,该用户也是不受影响的。
**查询缓存**
连接建立后执行查询语句的时候会先查询缓存Mysql会先校验这个sql是否执行过以Key-Value的形式缓存在内存中Key是查询预计Value是结果集。如果缓存key被命中就会直接返回给客户端如果没有命中就会执行后续的操作完成后也会把结果缓存起来方便下一次调用。当然在真正执行缓存查询的时候还是会校验用户的权限是否有该表的查询条件。
Mysql 查询不建议使用缓存因为对于经常更新的数据来说缓存的有效时间太短了往往带来的效果并不好对于不经常更新的数据来说使用缓存还是可以的Mysql 8.0 版本后删除了缓存的功能,官方也是认为该功能在实际的应用场景比较少,所以干脆直接删掉了。
**分析器**
mysql 没有命中缓存那么就会进入分析器分析器主要是用来分析SQL语句是来干嘛的分析器也会分为几步
**第一步,词法分析**一条SQL语句有多个字符串组成首先要提取关键字比如select提出查询的表提出字段名提出查询条件等等。做完这些操作后就会进入第二步。
**第二步,语法分析**主要就是判断你输入的sql是否正确是否符合mysql的语法。
完成这2步之后mysql就准备开始执行了但是如何执行怎么执行是最好的结果呢这个时候就需要优化器上场了。
**优化器**
优化器的作用就是它认为的最优的执行方案去执行(虽然有时候也不是最优),比如多个索引的时候该如何选择索引,多表查询的时候如何选择关联顺序等。
**执行器**
当选择了执行方案后mysql就准备开始执行了首先执行前会校验该用户有没有权限如果没有权限就会返回错误信息如果有权限就会去调用引擎的接口返回接口执行的结果。
## 二、语句分析
### 2.1 查询语句
说了以上这么多那么究竟一条sql语句是如何执行的呢其实我们的sql可以分为2中一种是查询一种是更新增加更新删除。我们先分析下查询语句语句如下
```
select * from tb_student A where A.age='18' and A.name='张三';
```
结合上面的说明,我们分析下这个语句的执行流程:
* 先检查该语句是否有权限如果没有权限直接返回错误信息如果有权限在mysql8.0版本以前会先查询缓存以这条sql语句为key在内存中查询是否有结果如果有直接缓存如果没有执行下一步。
* 通过分析器进行词法分析提取sql语句的关键元素比如提取上面这个语句是查询select提取需要查询的表名为tb_student,需要查询所有的列查询条件是这个表的id='1'。然后判断这个sql语句是否有语法错误比如关键词是否正确等等如果检查没问题就执行下一步。
* 接下来就是优化器进行确定执行方案上面的sql语句可以有两种执行方案
a.先查询学生表中姓名为“张三”的学生然后判断是否年龄是18。
b.先找出学生中年龄18岁的学生然后再查询姓名为“张三”的学生。
那么优化器根据自己的优化算法进行选择执行效率最好的一个方案(优化器认为,有时候不一定最好)。那么确认了执行计划后就准备开始执行了。
* 进行权限校验,如果没有权限就会返回错误信息,如果有权限就会调用数据库引擎接口,返回引擎的执行结果。
### 2.2 更新语句
以上就是一条查询sql的执行流程那么接下来我们看看一条更新语句如何执行的呢sql语句如下
```
update tb_student A set A.age='19' where A.name='张三';
```
我们来给张三修改下年龄在实际数据库肯定不会设置年龄这个字段的不然要被技术负责人打的。其实条语句也基本上会沿着上一个查询的流程走只不过执行更新的时候肯定要记录日志啦这就会引入日志模块了mysql 自带的日志模块式binlog归档日志所有的存储引擎都可以使用我们常用的InnoDB引擎还自带了一个日志模块redo log我们就以InnoDB模式下来探讨这个语句的执行流程。流程如下
* 先查询到张三这一条数据,如果有缓存,也是会用到缓存。
* 然后拿到查询的语句,把 age 改为19然后调用引擎API接口写入这一行数据InnoDB引擎把数据保存在内存中同时记录redo log此时redo log进入prepare状态然后告诉执行器执行完成了随时可以提交。
* 执行器收到通知后记录binlog然后调用引擎接口提交redo log 为提交状态。
* 更新完成。
这里肯定有同学会问为什么要用两个日志模块用一个日志模块不行吗这就是之前mysql的模式了MyISAM引擎是没有redo log的那么我们知道它是不支持事务的所以并不是说只用一个日志模块不可以只是InnoDB引擎就是通过redo log来支持事务的。那么又会有同学问我用两个日志模块但是不要这么复杂行不行为什么redo log 要引入prepare预提交状态这里我们用反证法来说明下为什么要这么做
* **先写redo log 直接提交,然后写 binlog**假设写完redo log 后机器挂了binlog日志没有被写入那么机器重启后这台机器会通过redo log恢复数据但是这个时候bingog并没有记录该数据后续进行机器备份的时候就会丢失这一条数据同时主从同步也会丢失这一条数据。
* **先写binlog然后写redo log**假设写完了binlog机器异常重启了由于没有redo log本机是无法恢复这一条记录的但是binlog又有记录那么和上面同样的道理就会产生数据不一致的情况。
如果采用redo log 两阶段提交的方式就不一样了写完binglog后然后再提交redo log就会防止出现上述的问题从而保证了数据的一致性。那么问题来了有没有一个极端的情况呢假设redo log 处于预提交状态binglog也已经写完了这个时候发生了异常重启会怎么样呢
这个就要依赖于mysql的处理机制了mysql的处理过程如下
* 判断redo log 是否完整,如果判断是完整的,就立即提交。
* 如果redo log 只是预提交但不是commit状态这个时候就会去判断binlog是否完整如果完整就提交 redo log, 不完整就回滚事务。
这样就解决了数据一致性的问题。
## 三、总结
* Mysql 主要分为Server曾和引擎层Server层主要包括连接器、查询缓存、分析器、优化器、执行器同时还有一个日志模块binlog这个日志模块所有执行引擎都可以共用。
* 引擎层是插件式的目前主要包括MyISAM,InnoDB,Memory等。
* sql等执行过程分为两类一类对于查询等过程如下权限校验---》查询缓存---》分析器---》优化器---》权限校验---》执行器---》引擎
* 对于更新等语句执行流程如下:分析器----》权限校验----》执行器---》引擎---redo log prepare---》binlog---》redo log commit
## 四、参考
* 《一起构建Mysql知识网络》