mysql:一条SQL查询语句是如何执行的?( 二 )


二 语句分析
2.1 查询语句
说了以上这么多 。那么究竟一条 sql 语句是如何执行的呢?其实我们的 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 引擎还自带了一个日志模块 redo log(重做日志) 。我们就以 InnoDB 模式下来探讨这个语句的执行流程 。流程如下:
先查询到张三这一条数据 。如果有缓存 。也是会用到缓存 。
然后拿到查询的语句 。把 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 日志只能用来归档 。
并不是说只用一个日志模块不可以 。只是 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 是否完整 。如果判断是完整的 。就立即提交 。

推荐阅读