简介

两阶段提交是 用来保证分布式系统中,事务提交 atomic 的协议。该协议可以在许多暂时的系统错误中达到 atomic 的目的。但是它不能适应所有情况,在极少数情况下,需要进行手工干预。

概念

在整个系统中,每个节点可能是以下两种角色中的一个

  • coordinator - 协调者
    • 类似于其他协议中的 master/leader 等,接收外部请求,并同步给其他节点
  • participants - 参与者
    • 分布式系统中,除去 coordinator 之外的其他节点

该协议有以下三个假设

  1. 节点的存储系统是稳定的,并且使用 write-ahead log
  2. 节点永远不会 crash
  3. 节点的数据不会因为 crash 造成丢失或损坏
  4. 任意两个节点可以相互通信

很明显,这些假设对现在分布式系统来说基本是不可能的。

图解

image.png

基本算法

prepare phase

  1. coordinator 发送一个 prepare 信息给系统中所有其他的 participants,并且等待所有 participants 的应答
  2. participant 收到 prepare 请求后,将相关修改写入 log;——redo log 和 undo log
  3. participant 回复 Yes/No

    commit phase

    所有 participants 都回复 Yes

  4. coordinator 发送 commit 信息给所有的 participants

  5. participant 完成 prepare 阶段的请求,并释放事务使用到的锁和其他资源
  6. participant 回复 ACK 给 coordinator
  7. 当 coordinator 收到所有 participants 的 ACK 信息时,就可以回复客户端,表示完成了这次请求。

    任意一个 participant 回复 No - 或超时/无应答

  8. coordinator 发送 rollback 信息给所有的 participants

  9. participant 取消这次事务(使用 undo log),并释放所有的锁和其他资源
  10. participant 发送 ACK 给 coordinator
  11. 当 coordinator 收到所有 participants 的 ACK 信息时,就回滚这次事务

    异常状态恢复

    如果任意一个节点重启,并且 reachable 时,coordinator 服务都可以检查磁盘上的 commit log,并重发请求。
  • coordinator: 如果没有 commit log,则发送 abort
    • 还没有发送任何的 commit 信息给 participant
  • participant
    • 没有回复 Yes 的 log,则 abort
      • participant 在 prepare 阶段没有回复 Yes,所以不需要 commit
    • 如果有回复 Yes 的 log,则继续等待 coordinator 的 commit or rollback
      • 可能会造成 block

        participant 在发送 response 前奔溃

        image.png

        participant 发送 response 之后奔溃

        image.png

        participant 发送了回复,但是 coordinator 没有收到

        image.png

        coordinator 在发送 prepare 前奔溃

        image.png

        coordinator 在发送 prepare 之后奔溃

        image.png

        coordinator 在收到 vote 后奔溃

        image.png

        coordinator 在发送一个 commit 后奔溃(没有发送给所有 participant)

        image.png

        真的需要一个 coordinator 吗?

        image.png

        一个转账的例子——跨系统,无法重新选主

        下图为不使用两阶段提交的做法:
        image.png
        可能存在的问题
  1. A 账号余额不足
  2. B 账号不存在
  3. 服务 A 或 B 奔溃了
  4. 服务 A 或 B 未收到相关请求
  5. 发送到 A 成功,但是发送到 B 失败了

下图为两阶段提交的做法:
image.png
虽然两阶段提交没有解决上述所有的问题(比如网络和奔溃就无法解决),但是却可以在异常时进行恢复。即服务 TC、A、B 有相应的 log 可以进行回滚,达到最终一致性。

安全性和最终一致性

  • 假设服务没有 crash,所有的服务都可以达到一致的状态。
  • 假设中间发生了异常,每一个 participant 都可以被修复,并最终达到一个一致的状态。

    缺点

  1. 最大的缺点是,这是一个 blocking 的协议。即,如果 coordinator 永久的失败了,一些 participants 将没有机会完成他们的事务(当 participant 在第一阶段 发送一个 OK 给 coordinator 时,就会 block 直到收到 commit 或 rollback)。
  2. 如果 coordinator 和 participant 都在 commit 阶段失败了,则无法可靠的恢复。
    1. 如果只有其中一个失败了,可以根据另一个的 log 进行恢复
    2. 如果两个都失败了,此时如果选择了一个新的 coordinator,则 coordinator 无法确认 participant 是否应该 commit,只能等待所有其他成员全都回复后在做决定——即 block

block 原因,participants 必须等待 coordinator 的应答;反之也是如此。

参考

https://www.cs.princeton.edu/courses/archive/fall16/cos418/docs/L6-2pc.pdf
https://en.wikipedia.org/wiki/Two-phase_commit_protocol
https://www.techopedia.com/definition/1252/two-phase-commit