SQL语句在MySQL中的执行过程
目录
一MySQL基础架构分析
1.1MySQL基本架构概览
1.2Server层基本组件介绍
二语句分析
2.1查询语句
2.2更新语句
三总结
一MySQL基础架构分析
1.1MySQL基本架构概览
先简单介绍一下下图涉及的一些组件的基本作用帮助大家来理解这幅图。
连接器:身份认证和权限相关(登录MySQL过程)
查询缓存:执行查询语句的时候,会先查询缓存(MySQL8.0版本后移除,因为这个功能不太实用)。
分析器:没有命中缓存的话,SQL语句就会经过分析器,分析器说白了就是要先看你的SQL语句要干什么,再检查你的SQL语句语法是否正确。
优化器:按照MySQL认为最优的方案去执行。
执行器:执行语句,然后从存储引擎返回数据。
简单来说MySQL主要分为Server层和存储引擎层:
Server层:主要包括连接器,查询缓存,分析器,优化器,执行器等,所有跨存储引擎的功能都在这一层实现,比如存储过程,触发器,视图,函数等,还有一个通用的日志模块binlog日志模块。
存储引擎:主要负责数据的存储和读取,采用可以替换的插件式架构,支持InnoDB,MyISAM,Memory等多个存储引擎,其中InnoDB引擎有自己的日志模块,现在常用的存储引擎是InnoDB。
1.2Server层基本组件介绍
1)连接器
连接器主要和身份认证和权限相关的功能相关,就好比一个级别很高的门卫一样。
主要负责用户登录数据库,进行用户的身份认证,包括校验账户密码,权限等操作,如果用户账户密码已通过,连接器回到权限表中查询该用户的所有权限,之后在这个连接里的权限逻辑等判断都是会依赖此时读取到的权限数据,也就是说,后续只要这个连接不断开,即使管理员修改了该用户的权限,该用户的也是不受影响的。
2)查询缓存(MySQL8.0版本后移除)
查询缓存主要用来缓存我们所执行的SELECT语句以及该语句的结果集。
连接建立后,执行查询语句的时候,会先查询缓存,MySQL会先校验这个SQL是否执行过,以Key-value的形式缓存在内存中,Key是查询语句,Value是结果集,如果缓存被命中的话,就会直接返回哥给客户端,如果没有命中,就会执行后续的操作,完成后也会把结果缓存起来,方便下一次调用。当然在真正执行缓存查询的时候还是会校验用户的权限,是否有该表的查询条件。
MySQL查询不建议使用缓存,因为查询缓存失效在实际业务场景中可能会非常频繁,假如你对一个表更新的话,这个表上的所有的查询缓存都会被清空。对于不经常更新,使用缓存还是可以的。
所以,一般在大多数情况下我们都是不推荐去使用查询缓存的。
3)分析器
MySQL没有命中缓存,那么就会进入分析器,分析器主要是用来分析SQL语句是干嘛的,分析器也会分为几步:
第一步,词法分析,一条SQL语句有多个字符串组成,首先要提取关键字,比如select,提出查询的表,提出查询条件等等。做完操作后,就会进入到第二步。
第二步,语法分析,主要就是判断你输入的SQL是否正确,是否符合MySQL的语法。
完成这两步,MySQL就准备开始执行了,但是如何执行了,怎么执行是最好的结果呢,下一步交给优化器。
4)优化器
优化器的作用就是它认为的最优的执行方案去执行(有时候可能也不是最优),比如多个索引的时候该如何选择索引,多表查询的时候如何选择关联顺序等。
可以说,经过了优化器之后可以说这个语句具体该如何执行就已经定下来。
5)执行器
当选择了执行方案后,MySQL 就准备开始执行了,首先执行前会校验该用户有没有权限,如果没有权限,就会返回错误信息,如果有权限,就会去调用引擎的接口,返回接口执行的结果。
二语句分析
2.1查询语句
其实我们的SQL可以分为两种,一种是查询,一种是更新(增加,修改,删除)。先分析查询语句,语句如下:
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更新语句
接下来看一条更新语句是如何执行的
update tb_student A set A.age='19' where A.name=' 张三 ';
我们来给张三修改下年龄,在实际数据库肯定不会设置年龄这个字段的。其实这条语句也基本上沿着上一个查询的流程走,只不过执行更新的时候肯定要记录日志的,这就会引入日志模块,MySQL自带的日志模块是binlog(归档日志),所有的存储引擎都可以使用,我们常用的InnoDB引擎还自带了一个日志模块redolog(重做日志)。
- 先查询到张三这一条数据,不会走查询缓存,因为更新语句会导致与该表相关的查询缓存失败。
- 然后拿到查询的语句,把age改为19,然后调用引擎API接口,写入这一行数据的,InnoDB引擎把数据保存在内存中,同时记录redolog,此时redolog进入prepare状态,然后告诉执行器,执行完了,随时可以提交。
- 执行器收到通知后记录binlog,然后调用引擎接口,提交redo log为提交状态。
这里肯定会有同学问,为什么要用两个日志模块,用一个日志模块不行吗?
这是因为最开始 MySQL 并没有 InnoDB 引擎(InnoDB 引擎是其他公司以插件形式插入 MySQL 的),MySQL 自带的引擎是 MyISAM,但是我们知道 redo log 是 InnoDB 引擎特有的,其他存储引擎都没有,这就导致会没有 crash-safe 的能力(crash-safe 的能力即使数据库发生异常重启,之前提交的记录都不会丢失),binlog 日志只能用来归档。
并不是说只用一个日志模块不可以,只是InnoDB引擎就是通过redolog来支持事务的。那么,又会有同学问,我用两个日志模块,但是不要这么复杂行不行,为什么redo log要引入prepare预提交状态呢,这里我们使用反证法说明为什么这么做?
- 先写redolog直接提交,然后写binlog,假设写完redolog后,机器挂了,binlog日志没有被写入,那么机器在重启后,这台机器会通过redolog恢复数据,但是这个时候binlog并没有记录该数据,后续进行机器备份的时候,就会丢失这一条数据,同时主从同步也会丢失这一条数据。
- 先写 binlog,然后写 redo log,假设写完了 binlog,机器异常重启了,由于没有 redo log,本机是无法恢复这一条记录的,但是 binlog 又有记录,那么和上面同样的道理,就会产生数据不一致的情况。
注:redolog是存储引擎级别的日志,用于记录事务的修改操作,主要用于InnoDB的崩溃恢复。binlog是服务器级别的日志,用于记录所有对数据库的修改操作,包括来自不同引擎的操作,主要用于主从同步和数据恢复。
注:每个表可以选择不同的引擎,所以一个数据库可以有多个引擎。
如果采用redo log两阶段提交方式就不一样了,写完binlog后,然后再提交redo log就会防止出现上述的问题,从而保证了数据的一致性。那么问题来了,有没有一个极端的情况呢?假设redo log处于预提交的状态,这个就要依赖MySQL的处理机制了,
1.判断redo log是否完整,如果判断是完整的,就立即提交
2.如果redo log只是预提交但不是commit状态,这个时候就会去判断binlog是否完整,如果完整就提交redo log不完整就会滚事务。
三总结
- MySQL 主要分为 Server 层和引擎层,Server 层主要包括连接器、查询缓存、分析器、优化器、执行器,同时还有一个日志模块(binlog),这个日志模块所有执行引擎都可以共用,redolog 只有 InnoDB 有。
- 引擎层是插件式的,目前主要包括,MyISAM,InnoDB,Memory 等。
- 查询语句的执行流程如下:权限校验(如果命中缓存)--->查询缓存--->分析器--->优化器--->权限校验--->执行器--->引擎
- 更新语句执行流程如下:分析器---->权限校验---->执行器--->引擎---redo log(prepare 状态)--->binlog--->redo log(commit 状态)