【MySQL】详解binlog和redolog两阶段提交

在 MySQL 的事务执行过程中,binlog 和 redo log(重做日志)协同工作来确保事务的持久性和数据一致性。MySQL 使用一种称为 【两阶段提交】 的机制来确保这两个日志之间的一致性,以避免在崩溃时出现数据不一致的情况。 在我前面的 博客 中也有详细的介绍这两种日志,感兴趣的可以点击链接查阅。

1. redo log 和 binlog 的作用

redo log(重做日志):它是 InnoDB 存储引擎的日志,主要用于实现 事务的持久性崩溃恢复。当事务在 MySQL 中被提交时,数据并不会立即写入磁盘,而是先写入内存页,再通过 redo log 记录修改。在系统崩溃后,InnoDB 会通过 redo log 恢复未写入磁盘的事务,保证事务的持久性(即满足 事务的 ACID 中的 D:Durability)。

binlog(二进制日志):它是 MySQL Server 层的日志,记录所有导致数据修改的 SQL 语句。binlog 主要用于 主从复制基于时间点的恢复,使得主库和从库保持一致。

由于这两个日志分别在 MySQL Server 层和 InnoDB 存储引擎层,MySQL 需要确保这两个日志的状态在事务提交时保持一致。

为什么会发生这些不一致?

复制代码
redo log 负责在主库崩溃时恢复数据,因此它影响的是 主库的数据持久性。
binlog 主要用于 主从复制,确保事务的操作可以复制到从库,从而影响 从库的数据一致性。

由于这两份日志分别负责不同的功能,如果在事务提交过程中,redo log 和 binlog 的刷盘过程不同步(即出现了 "半成功" 状态),就会导致主库和从库之间的数据不一致:

复制代码
当 redo log 刷入磁盘成功,但 binlog 未写入磁盘时:主库的数据更新成功,但由于 binlog 丢失,从库不会执行相应的更新。
当 binlog 刷入磁盘成功,但 redo log 未写入磁盘时:从库的数据更新成功,但由于主库没有完成事务提交,主库回滚了这次更新。

这两种情况下,主库和从库的数据都会发生不一致,原因是 redo log 和 binlog 分别控制了主库的事务持久性和从库的事务一致性,如果它们之间的操作没有严格同步,主从数据就可能出现不一致。

看下面第2节的例子。

2. 为什么需要两个阶段提交(保持两种日志的一致)

假设 id =1这行数据的字段 name 的值原本是'lisi',然后执行 UPDATE user SET name = 'zhangsan' WHERE id = 1;如果在持久化redo log 和 binlog 两个日志的过程中,出现了半成功状态,那么就有两种情况

情况一:redo log 刷入磁盘成功,但 binlog 没有写入磁盘

过程

  • 执行 UPDATE user SET name = 'zhangsan' WHERE id = 1;,首先将事务的修改(name 从lisi 变为 zhangsan)写入 redo log 的 prepare 状态并刷入磁盘。
  • 此时,redo log 已经确保数据的持久性,即便 MySQL 发生崩溃,重启后依然能通过 redo log 恢复内存中的数据,将 name 恢复到新的值 zhangsan。
  • 但在 redo log 刷入磁盘之后,MySQL 在还没有来得及写入 binlog 的时候宕机。

重启后的结果

  • 主库:由于 redo log 已经持久化,事务恢复时会根据 redo log 的内容将 id = 1 这一行的 name 字段恢复到新值 zhangsan。
  • 从库:binlog 没有写入,因此在主库宕机之前,这条更新语句没有记录在 binlog 中。从库通过复制机制读取的是主库上次宕机前的 binlog,由于 binlog 丢失了这条更新语句,从库的 name 字段仍然是旧值 lisi。

问题:主库的 name 值是 zhangsan,从库的 name 值是 lisi,导致主从数据不一致。

=====================================================================================================

情况二:binlog 刷入磁盘成功,但 redo log 没有刷入磁盘

过程

  • 执行 UPDATE user SET name = 'zhangsan' WHERE id = 1;,先将 SQL 语句记录在 binlog 中,并成功将 binlog 刷入磁盘。
  • 在 binlog 刷入磁盘后,MySQL 发生崩溃,而此时 redo log 还没有提交成功,即 redo log 还处于 prepare 状态,没有完成持久化。

重启后的结果:

  • 主库:由于 redo log 还没有提交,所以在崩溃恢复时,MySQL 发现该事务并没有真正提交,因此会回滚这次事务。重启后,主库的 id = 1 这一行 name 字段会保持旧值 lisi。
  • 从库:由于 binlog 已经刷入磁盘,并且在主从复制时会将 binlog 传递给从库,所以从库会执行 binlog 中记录的这条更新语句,结果是从库将 id = 1 这一行的 name 字段更新为zhangsan。

问题:主库的 name 值是lisi,而从库的 name 值是 zhangsan,同样会导致主从数据不一致。

3. 两阶段提交的执行过程

在事务提交过程中,MySQL 的 binlog 和 InnoDB 的 redo log 需要通过两阶段提交机制来保持一致性。这个过程大致如下:
【图片来自:小林coding】

阶段 1:准备阶段(Prepare Phase)

  1. 记录 redo log:当一个事务执行并准备提交时,InnoDB 会将事务的修改先记录到 redo log 的 prepare 状态。这时的 redo log 还没有真正提交,但已经将所有修改持久化到了日志中。该状态确保在崩溃恢复时,系统能够根据这个 redo log 的 prepare 状态判断事务的执行情况。

  2. 写入内存:事务的数据修改通常首先在内存中操作(内存页或缓冲池),此时数据还没有被写入磁盘,但 redo log 的准备记录确保了这些修改可以在崩溃后恢复。

  3. 等待 binlog 的同步:在这一步,InnoDB 已经做好了写入日志的准备,但事务还没有完全提交,系统等待 binlog 的同步。

阶段 2:提交阶段(Commit Phase)

  1. 写入 binlog:MySQL 将该事务的修改操作(如 INSERT、UPDATE、DELETE)记录到 binlog 中,并根据配置(例如 sync_binlog)决定是否将 binlog 刷盘写入磁盘。这一步是为了确保在主从复制时,能够完整地同步所有操作。

  2. 提交 redo log:当 binlog 写入成功后,将 redo log 的状态从 prepare 改为 commit,并将日志数据刷入磁盘。这一过程确保事务的持久化和一致性。

  3. 事务提交完成:此时,事务的所有数据修改已经成功写入到 redo log 和 binlog 中,事务被正式提交。

4. 为什么两阶段提交不会出现重启后不一致的情况

【图片来自:小林coding】

不管是时刻 A(redo log 已经写入磁盘, binog 还没写入磁盘),还是时刻 B(redo log 和 binlog 都已经写入磁盘,还没写入 commit 标识)崩溃,此时的redo log 都处于 prepare 状态。

在 MySQL 重启后会按顺序扫描 redo log 文件,碰到处于 prepare 状态的 redo log,就拿着 redo log 中的XID 去 binlog 查看是否存在此 XID:

  • 如果 binlog 中没有当前内部 XA 事务的 XID,说明 redolog 完成刷盘,但是 binlog 还没有刷盘,则回滚事务。对应时刻 A 崩溃恢复的情况。
  • 如果 binlog 中有当前内部 XA 事务的 XID,说明 redolog 和 binlog 都已经完成了刷盘,则提交事务对应时刻 B 崩溃恢复的情况。

可以看到,对于处于 prepare 阶段的 redo log,即可以提交事务,也可以回滚事务,这取决于是否能在binlog 中查找到与 redo log 相同的 XID,如果有就提交事务,如果没有就回滚事务。这样就可以保证redo log 和 binlog 这两份日志的一致性了。

所以说,两阶段提交是以 binlog 写成功为事务提交成功的标识,因为 binlog 写成功了,就意味着能在binlog 中查找到与 redo log 相同的 XID。

相关推荐
Mike117.3 小时前
GBase 8a 日期边界写法和时间窗口取数偏差
数据库
SPC的存折4 小时前
1、Redis数据库基础
linux·运维·服务器·数据库·redis·缓存
MatrixOrigin8 小时前
数据库没有死,只是范式变了
数据库·oracle
羊小蜜.9 小时前
Mysql 13: 触发器全解——创建、查看、使用与注意事项
数据库·mysql·触发器
阿里加多9 小时前
第 1 章:Go 并发编程概述
java·开发语言·数据库·spring·golang
ShiJiuD6668889999 小时前
Mysql 进阶
数据库·mysql
一 乐9 小时前
物流信息管理|基于springboot + vue物流信息管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·物流信息管理系统
l1o3v1e4ding10 小时前
排查linux CentOS7.6的mysql磁盘 I/O 延迟过高问题
linux·运维·mysql