知道Mysql的更新流程可以知道Mysql的重做日志(redo)和binlog日志,理解Mysql为什么是二阶段提交,是解决Mysql redo日志binlog日志面试的前提。

redo日志

redo日志

redo 日志是Mysql InnoDB引擎的一种日志,保存在磁盘。在Mysql 进行更新的时候不是立即进行数据库的磁盘IO操作,而是将它记录到一个redo日志中,然后可以在Mysql闲时更新到数据库,减少Mysql数据频繁IO操作的压力。

binlog 日志

binlog日志 属于Mysql Server层面的日志,以二进制的形式记录语句的原始逻辑,常用于数据恢复,主从复制等。

Mysql 更新流程如图所示

Mysql 更新流程

详细流程如下:

  1. 客户端发送updatesql语句,语法分析器识别出update语句;
  2. 执行器让innoDB查找id=2的数据。
  3. InnoDB引擎查找id=2的数据
  4. InnoDB引擎返回id=2的数据
  5. 执行器将name修改为'测试'
  6. 执行器通知InnoDB引擎修改数据到Innodb内存
  7. InnoDB引擎修改结果更新到缓存
  8. Innodb引擎 添加一条redo日志,状态设置prepare。
  9. Innodb引擎通知执行器修改完成,可以提交事务
  10. 执行器将日志"update user set name='测试' where id=2" 写入binlog
  11. 执行器通知InnoDB引擎提交事务。
  12. Innodb引擎提交事务,将redo日志事务prepare更改为commit状态,并将内存中的数据刷新到磁盘。刷新时机参考:innodb_flush_log_at_trx_commit。

这里只需要记住大概流程即可。

  1. Innodb引擎先查找要修改的数据并将更新的数据先写到一个 redo 日志中,将状态设置为prepare,然后通知执行器
  2. Server层的执行器得到通知,并将日志写入binlog,并通知Innodb引擎可以提交了
  3. Innodb引擎将redo日志改为commit状态,并根据刷盘策略刷新到磁盘。

上面的过程中redo提交的过程分为2个步骤,第一步是prepare,第二步是commit,即两阶段提交,如图所示:

Mysql 二阶段提交

为什么是两个阶段提交呢?

为了解决数据的不一致问题,在上面的例子中

  • 如果在第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的时候系统崩溃,重启后和上一种情况一样处理。

由此可见,两阶段提交能够确保数据的一致性。