MySQL--SQL执行过程

SQL语句执行过程

在谈SQL语句的执行流程之前,我们需要先知道MySQL的基础架构。

MySQL基础架构


MySQL基础架构:
image
MySQL主要分为Server层和存储引擎层:
Server层: 主要包括连接器、查询缓存、分析器、优化器、执行器等,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图,函数等,还有一个通用的日志模块 binglog 日志模块。
存储引擎:主要负责数据的存储和读取,采用可以替换的插件式架构,支持 InnoDB、MyISAM、Memory 等多个存储引擎,其中 InnoDB 引擎有自有的日志模块 redolog 模块。现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始就被当做默认存储引擎了。

MySQL基本组件

连接器:
主要负责用户登录数据库,进行用户的身份认证,包括校验账户密码,权限等操作,如果用户账户密码已通过,连接器会到权限表中查询该用户的所有权限,之后在这个连接里的权限逻辑判断都是会依赖此时读取到的权限数据,也就是说,后续只要这个连接不断开,即时管理员修改了该用户的权限,该用户也是不受影响的。

查询缓存:
查询缓存主要用来缓存我们所执行的 SELECT 语句以及该语句的结果集。执行查询语句的时候,会先查询缓存(MySQL 8.0 版本后移除,因为这个功能不太实用)。

分析器:
MySQL 没有命中缓存,那么就会进入分析器,分析器主要是用来分析 SQL 语句是来干嘛的,分析器也会分为几步:第一步,词法分析,一条SQL语句有多个字符串组成,首先要提取关键字,比如 select,提出查询的表,提出字段名,提出查询条件等等。做完这些操作后,就会进入第二步。第二步,语法分析,主要就是判断你输入的sql是否正确,是否符合 MySQL的语法。

优化器:
完成上面 2 步之后,MySQL 就准备开始执行了,但是如何执行,怎么执行是最好的结果呢?这个时候就需要优化器上场了。优化器的作用就是用它认为的最优的执行方案去执行(有时候可能也不是最优,这篇文章涉及对这部分知识的深入讲解),比如多个索引的时候该如何选择索引,多表查询的时候如何选择关联顺序等。可以说,经过了优化器之后可以说这个语句具体该如何执行就已经定下来。

执行器:
当选择了执行方案后,MySQL 就准备开始执行了,首先执行前会校验该用户有没有权限,如果没有权限,就会返回错误信息,如果有权限,就会去调用引擎的接口,返回接口执行的结果。

具体语句分析

通常我们将SQL语句分为两类,一类是查询语句,另一类是更新(增加、更新、删除)语句。

查询语句

比如:select * from tb_student A where A.age='18' and A.name=' 张三 ';
流程如下:
image

  • 检查语句权限:先检查该语句是否有权限,如果没有权限,直接返回错误信息,如果有权限进入下一步。
  • 查询缓存:在 MySQL8.0 版本以前,会先查询缓存,以这条sql语句为key在内存中查询是否有结果,如果有直接查询缓存,返回结果;如果没有,执行下一步。
  • 分析器进行词法分析:通过分析器进行词法分析,提取sql语句的关键元素,比如上面这个语句,需要提取:具体操作(查询select),表(tb_student),需要查询的列(name,age),查询条件(age='18',name='张三')
  • 分析器进行语法分析:接下来判断这个sql语句是否语法错误,比如关键词是否正确等。
  • 优化器进行优化:优化器根据优化算法选择最有效率的一个方案,比如上面的语句,可以有两种执行方案:
1. 先查询学生表中name为“张三”的学生,然后判断年龄是否为18。
2. 先查询学生中年龄是18岁的学生,然后再查询姓名为“张三”的学生。
  • 准备执行:进行优化了之后,就要确定执行方案,准备开始执行。
  • 返回结果:进行权限校验,如果没有权限就会返回错误信息,如果有权限就会调用数据库引擎接口,返回引擎的执行结果。

更新语句

update tb_student A set A.age='19' where id = 1;
image
具体过程如下:
先查询到id=1的这一条数据,如果有缓存,也是会用到缓存。然后拿到查询的语句,把age改为 19,然后调用引擎 API 接口,写入这一行数据,InnoDB 引擎把数据保存在内存中,同时记录 redo log,此时 redo log 进入 prepare 状态,然后告诉执行器,执行完成了,随时可以提交。执行器收到通知后记录binlog,然后调用引擎接口,提交 redo log为提交状态。

为什么要用到两个日志模块?
这是因为最开始 MySQL 并没与 InnoDB 引擎( InnoDB 引擎是其他公司以插件形式插入 MySQL 的) ,MySQL 自带的引擎是 MyISAM,但是我们知道 redo log 是 InnoDB 引擎特有的,其他存储引擎都没有,这就导致会没有 crash-safe 的能力(crash-safe 的能力即使数据库发生异常重启,之前提交的记录都不会丢失),binlog 日志只能用来归档。
为什么 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),这个日志模块所有执行引擎都可以共用,redolog 只有 InnoDB 有。
  • 引擎层是插件式的,目前主要包括,MyISAM,InnoDB,Memory 等。
  • 查询语句的执行流程如下:权限校验(如果命中缓存)---》查询缓存---》分析器---》优化器---》权限校验---》执行器---》引擎
  • 更新语句执行流程如下:分析器----》权限校验----》执行器---》引擎---redo log(prepare 状态---》binlog---》redo log(commit状态)
posted @ 2021-06-16 11:21  sinlearn  阅读(100)  评论(0编辑  收藏  举报