当向MySQL接受一个请求的时候,MySQL到底做了什么
1. 接收上层传送的SQL语句 2. 语法验证模块:验证语句语法是否满足SQL MODE 3. 语句检查:判断SQL语句的类型 DDL:数据定义语句 DCL:数据控制语句 DML:数据操作语句 DQL:数据查询语句 4. 权限检查:用户对库表的权限 5. 解析器:对语句执行前的预处理,生产执行计划 6. 优化器:根据多种执行计划,进行判断,选择最优的执行计划,代价模型资源(CPU,IO,MEM)评估 7. 执行器:根据最优的执行计划 执行SQL语句,返回结果
1、查询语句
注意:查询缓存在MySQL 8.0版本之后被移除
select * from tb_student A where A.age='18' and A.name=' 张三 ';
1)先检查该语句是否有权限,如果没有权限,直接返回错误信息,如果有权限,在 MySQL8.0 版本以前,会先查询缓存,以这条 sql 语句为 key 在内存中查询是否有结果,如果有直接返回缓存数据,如果没有,执行下一步。
2)通过分析器进行词法分析,提取 sql 语句的关键元素,比如提取上面这个语句是查询 select,提取需要查询的表名为 tb_student,需要查询所有的列,查询条件是这个表的 id='1'。然后判断这个 sql 语句是否有语法错误,比如关键词是否正确等等,如果检查没问题就执行下一步。
3)接下来就是优化器进行确定执行方案,上面的 sql 语句,可以有两种执行方案:
- 先查询学生表中姓名为“张三”的学生,然后判断是否年龄是 18。
- 先找出学生中年龄 18 岁的学生,然后再查询姓名为“张三”的学生。
那么优化器根据自己的优化算法进行选择执行效率最好的一个方案(优化器认为,有时候不一定最好)。那么确认了执行计划后就准备开始执行了。
4)进行权限校验,如果没有权限就会返回错误信息,如果有权限就会调用数据库引擎接口,返回引擎的执行结果。
执行流程如下:权限校验(如果命中缓存)-->查询缓存-->分析器-->优化器-->权限校验-->执行器-->存储引擎。
注意:查询缓存在MySQL 8.0版本之后被移除
2、更新语句
update tb_student A set A.age='19' where A.name=' 张三 ';
其实这条语句也基本上会沿着上一个查询的流程走,只不过执行更新的时候肯定要记录日志,这就会引入日志模块了,MySQL 自带的日志模块式 binlog(归档日志) ,所有的存储引擎都可以使用,我们常用的 InnoDB 引擎还自带了一个日志模块 redo log(重做日志),我们就以 InnoDB 模式下来探讨这个语句的执行流程。流程如下:
1)先查询到张三这一条数据,如果有缓存,也是会用到缓存。
2)拿到查询的语句后,把 age 改为 19,然后调用存储引擎 API 接口,写入这一行数据,InnoDB 引擎把数据保存在内存中,同时记录 redo log,此时 redo log 进入 prepare 状态,然后告诉执行器,执行完成了,随时可以提交。
3)执行器收到通知后记录 binlog,然后调用存储引擎接口,提交 redo log 为提交状态。更新完成。
执行流程如下:分析器-->权限校验-->执行器-->存储引擎-->redo log(prepare状态)-->binlog-->redo log(commit状态)
为什么要用两个日志模块,用一个日志模块不行吗?
这是因为最开始 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 恢复数据,但是这个时候 binlog 并没有记录该数据,后续进行机器备份的时候,就会丢失这一条数据,同时主从同步也会丢失这一条数据。
先写 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, 不完整就回滚事务。
这样就解决了数据一致性的问题。