知道Mysql的更新流程可以知道Mysql的重做日志(redo)和binlog日志,理解Mysql为什么是二阶段提交,是解决Mysql redo日志binlog日志面试的前提。
redo日志
redo 日志是Mysql InnoDB引擎的一种日志,保存在磁盘。在Mysql 进行更新的时候不是立即进行数据库的磁盘IO操作,而是将它记录到一个redo日志中,然后可以在Mysql闲时更新到数据库,减少Mysql数据频繁IO操作的压力。
binlog 日志
binlog日志 属于Mysql Server层面的日志,以二进制的形式记录语句的原始逻辑,常用于数据恢复,主从复制等。
Mysql 更新流程如图所示
详细流程如下:
- 客户端发送updatesql语句,语法分析器识别出update语句;
- 执行器让innoDB查找id=2的数据。
- InnoDB引擎查找id=2的数据
- InnoDB引擎返回id=2的数据
- 执行器将name修改为'测试'
- 执行器通知InnoDB引擎修改数据到Innodb内存
- InnoDB引擎修改结果更新到缓存
- Innodb引擎 添加一条redo日志,状态设置prepare。
- Innodb引擎通知执行器修改完成,可以提交事务
- 执行器将日志"update user set name='测试' where id=2" 写入binlog
- 执行器通知InnoDB引擎提交事务。
- Innodb引擎提交事务,将redo日志事务prepare更改为commit状态,并将内存中的数据刷新到磁盘。刷新时机参考:innodb_flush_log_at_trx_commit。
这里只需要记住大概流程即可。
- Innodb引擎先查找要修改的数据并将更新的数据先写到一个 redo 日志中,将状态设置为prepare,然后通知执行器
- Server层的执行器得到通知,并将日志写入binlog,并通知Innodb引擎可以提交了
- Innodb引擎将redo日志改为commit状态,并根据刷盘策略刷新到磁盘。
上面的过程中redo提交的过程分为2个步骤,第一步是prepare,第二步是commit,即两阶段提交,如图所示:
为什么是两个阶段提交呢?
为了解决数据的不一致问题,在上面的例子中
- 如果在第8步提交redo日志,而第10步写binlog日志失败了,那么此时binlog和redo log日志不一致,这样会造成主从模式中,从设备是获取binlog日志的数据,会造成从设备的数据不一致。
- 如果在第12步提交redo日志,而第10步binlog日志写成功,12步写redo日志失败,这样会造成主从模式中,从设备有这条数据,主设备因为提交失败没有,会造成从设备的数据不一致。
我们来看二阶段的好处:
在上面的例子中
- 如果步骤9系统崩溃,即1阶段提交后崩溃,redo log 处于prepare时崩溃了。此时binlog还没有写,日志不会同步到从库。
- 如果步骤11系统崩溃,即写完binlog日志系统崩溃,在系统恢复的时候,首先检查 binlog 中的事务是否存在并且完整,如果存在且完整,则直接提交事务,如果不存在或者不完整,则回滚事务。
- 如果步骤12系统崩溃,即commit的时候系统崩溃,重启后和上一种情况一样处理。
由此可见,两阶段提交能够确保数据的一致性。