MySQL架构(三)mysql的两阶段提交

2024-05-21 17:15:47 浏览数 (1)

Mysql 的两阶段提交

在 MySQL架构(二)SQL 更新语句是如何执行的?中说到了 redo log 和 binlog 日志文件,在事务执行过程中,会分两个阶段写入这两份日志文件中,这也是为了保证两份日志之间的一致性,即维护 mysql 的数据一致性。

试想,如果不采用两阶段提交,会发生哪些情况?

由于 redo log 和 binlog 是两个独立的逻辑,不采用两阶段提交,有两种情况。

  • 情况 1:先写入 redo log 日志;再写入 binlog 日志。
  • 情况 2:先写入 binlog 日志;再写入 redo log 日志。

接下来我们用 MySQL架构(二)SQL 更新语句是如何执行的?中的例子来看看这两种方式会存在什么问题。

例子:update T set age = age 1 where ID = 2;

假设 ID=2 的行,字段 age=0 ,当执行该 update 语句时,第一个日志已经完成写入,但是第二个日志还没有写,或者还没有写完,在这个期间程序发生了异常奔溃(crash),这时该条更新的数据会出现怎样的情况呢?

情况1: 先写入 redo log 日志;再写入 binlog 日志

如果 redo log 写完,binlog 还没有写,或者还没有写完的时候,MySQL 进程突发异常崩溃。由于 redo log 已经写完,即便 mysql 崩溃,数据仍然能够恢复,所以恢复后该行数据中 age=1

但 binlog 日志并没有完成写入,并没有记录这个更新语句。因此,在我们进行备份日志的时候,导出的 binlog 日志中没有这条更新语句。当我们需要用这个 binlog 日志去恢复临时库时,由于日志中缺失这个更新语句,那么本次恢复的临时库就少了一次更新,得到的值为 age=0 ,如此就与原库 age=1 的值不同。

情况 2: 先写入 binlog 日志;再写入 redo log 日志

如果 binlog 日志写完, redo log 日志还没写,或者还没有写完的时候,mysql 发生异常崩溃。在恢复数据以后,由于 redo log 没有更新记录,所以这个事务无效,所以 age=0,没发生更新。

但是 binlog 日志里面已经完整记录了 age=1,在之后使用 binlog 日志恢复临时库时就多了一个更新事务,恢复后得到的值为 age=1,这时也与原库的值不同。

由上述情况我们可以看到,如果不采用“两阶段提交”的方式,数据库在发生异常需要恢复数据的时候,采用两种日志恢复的数据就不一致了。

两阶段过程异常崩溃处理

如果在两阶段提交的方式下,在 binlog 日志写完,事务在没有 commit 前,即 redo log 日志还没记录 commit 前,mysql 进程发生异常崩溃,MySQL 恢复数据的流程如下。

首先,我们看一下完整的两阶段提交流程,分为准备阶段和提交阶段。

  1. 在准备阶段,MySQL 先将数据修改写入 redo log 日志,并将其标记为 prepare 状态,即事务还处于未提交状态。再将对应的 SQL 语句写入 bin log 日志。
  2. 在提交阶段,事务完成提交,MySQL 将 redo log 日志标记为 commit 状态。然后根据 sync_binlog 参数,决定是否将 bin log 刷入磁盘。
    1. sync_binlog=1 时,将 bin log 刷入磁盘。

知道了两阶段提交流程后,我们再来看一下异常崩溃后,mysql 是如何恢复数据的。

  • 若 redo log 日志里记录了完整的事务,即已经处于 commit 状态,则直接提交恢复。
  • 若 redo log 日志里只记录了 prepare 状态的事务,则需要再判断对应的事务 binlog 日志是否存在并日志是否完整:
    • 如果 binlog 存在该事务日志并且是完整的,则提交事务恢复数据。
    • 否则,则回滚事务。

由上诉可知,在 binlog 日志写完,事务在没有 commit 前,mysql 进程发生异常崩溃,此时 mysql 会提交事务恢复数据。

0 人点赞