MySQL 两阶段提交

前面我们已经讲过 redo log 和 bin log 的相关知识了,那么在执行一条更新语句的时候,这两个日志是怎么相互配合的呢?

我们还是借助这条语句来进行分析:

update T set c=c+1 where ID=2
Java

当执行这条语句的时候,MySQL 的具体操作顺序是这样的:

执行 UPDATE T set c=c+1 where ID=2,代表事务开始。

事务首先会在 redo log 中记录即将对数据页进行的物理修改。

然后,数据库会将相应的 SQL 语句记录到 bin log 中,准备将其传递给其他节点(如果是主从复制环境)或做增量备份。

在执行过程中,redo log 会记录页面上的所有物理修改。例如,在这条 UPDATE 语句执行时,数据库首先会找到表 T 中 ID=2 的记录,并将修改的 c 字段值保存在 redo log 中,确保数据页的变更可以持久化到磁盘。

与此同时,bin log 会记录执行的 SQL 语句(UPDATE T set c=c+1 where ID=2;),用于记录逻辑变更。

实际上,上述过程就是两阶段提交的过程。

再来看一个例子,比如你要去银行给朋友转账 1000 元。那么转账这件事,实际上可以细分为两个阶段:

第一阶段(准备阶段):银行系统先确认是否可以完成转账

  1. 发起操作:你准备给朋友转账 1000 元。首先,你会登录银行的系统并提交转账请求。
  2. 银行的准备工作
  • 银行会先检查你账户中的余额是否足够支付 1000 元。
  • 银行也会检查目标账户是否有效,以及该笔转账是否符合银行的所有要求(如防止洗钱、账户问题等)。

在这一阶段,银行系统尚未实际扣款,只是确认你有足够的资金并且转账请求是合理的。

第二阶段(提交阶段):银行执行转账并完成操作

  1. 银行执行转账
  • 如果通过了银行的检查,银行会最终从你的账户中扣除 1000 元并将该金额存入朋友的账户。
  • 此时,银行将转账的记录同步到数据库中(即执行账务更新)。
  1. 最终确认:银行最终确认并将这笔转账交易记录(例如转账成功的消息)发送给你。此时,你的账户和你朋友的账户都更新了。

两阶段提交的具体过程:

第一阶段:准备阶段,当 MySQL 确认事务中的所有操作(包括对 T 表的 UPDATE)已经记录到 redo log 中,并且准备好将这些变更永久写入磁盘时,MySQL 会发出 PREPARE 命令,通知事务管理器该事务已经准备好。

此时,所有的物理修改已经通过 redo log 被持久化,但是还没有实际提交(bin log 中的日志尚未提交,数据尚未对外可见)。

如果此时发生崩溃,可以通过 redo log 来恢复数据。

第二阶段:提交阶段,在所有事务参与者确认准备好后(在主从复制中,所有的从库也需要确认接收到相应的日志),MySQL 会发出 COMMIT 命令,正式提交事务。

此时,redo log 记录的物理变更会被正式应用到磁盘上的数据页。

bin log 中的 SQL 语句(UPDATE T set c=c+1 where ID=2;)会被永久写入,标志着事务的成功提交。

所有变更将变得对外可见,其他数据库节点(如从库)通过 bin log 进行同步。

以上就是两阶段提交的全过程了。

了解完两阶段提交之后,我们可以想一下,为什么要分成两个阶段来提交呢?

如果先写 redo log,再写 bin log 会出现什么问题?

  • 这意味着事务的物理变化已经发生在磁盘上(数据已被更新),但 binlog 记录尚未写入。这时,主库的数据状态是更新的,而 binlog 还没有反映这些变化。
  • 如果此时主库崩溃,恢复时通过 redo log 恢复数据的物理更改,但 bin log 记录没有同步到从库。此时,从库无法正确回滚该事务,就会导致主从数据不一致。

如果先写 bin log,再写 redo log 会出现什么问题呢?

  • 在这种情况下,事务的操作已经记录到了 bin log 中,并且bin log 中的数据已经更新到磁盘了,那么从库将会接收到这些操作。但主库上的物理数据变更(写入 redo log)尚未完成。
  • 如果系统崩溃,redo log 中的操作无法恢复,虽然从库可能根据 binlog 接收到操作,但主库没有成功写入实际的数据变更,导致 主库的物理数据丢失,恢复时数据也会不一致。

可以看到,在持久化 redo log 和 binlog 这两份日志的时候,如果出现半成功的状态,就会造成主从环境的数据不一致性。这是因为 redo log 影响主库的数据,binog 影响从库的数据,所以 redo log 和 binlog必须保持一致才能保证主从数据一致。

MySQL为了避免出现两份日志之间的逻辑不一致的问题,使用了「两阶段提交」来解决,两阶段提交可以保证多个逻辑操作要不全部成功,要不全部失败,不会出现半成功的状态。

发表评论

后才能评论