两阶段提交协议

在分布式系统中每个节点都可以知道自己的操作是成功还是失败,但是无法知道其他节点的状态。为了保证一个事务的 ACID 特性,一个节点发生失败就要在所有节点上执行 rollback 操作。需要引入一个 协调者 来维护各个 参与者 的状态,以保证最终一致。

2pc 并不是万能的,需要满足一定的条件才可以使用:

  1. 一个节点是协调者,其他节点作为参与者,相互之前可以通信
  2. 每个节点要有 redo log 机制,而且存在持久化存储中
  3. 节点不会永久损坏,一定时间会重启恢复

Tow-phase Commit Protocol

首先引入几个概念:

  • 协调者:维护所有节点
  • 参与者:执行具体操作的节点
  • prepare phase:准备阶段,写入日志,资源加锁
  • commit phase:执行阶段,根据协调者指令执行,资源解锁

上图中 ① 和 ② 表示 prepare phase,③ 和 ④ 表示 commit phase。

在 prepare phase 阶段,协调者发出信息让参与者准备。参与者接受到信息以后一般会做两件事情:

  1. 根据需要执行的操作生成 redo 日志,用于后续 commit 或者 rollback
  2. 给所需的资源上锁,防止其他程序获取

参与者完成这两个操作会把结果通知协调者。

当协调者接受到参与者在 prepare phase 阶段的响应(无论 Yes 还是 No),就会进入 commit phase 阶段。在该阶段,如果接受到 prepare phase 的响应所有都是 Yes 时候,会发出 Commit 指令;只要收到一个 No,发出的就是 Rollback 指令。

参与者接受到协调者发出的 Commit 或者 Rollback 指令后会做两件事情:

  1. 执行 Commit 或者 Rollback
  2. 清理资源,解除锁的占用

的当这两部都完成以后,会回复 ACK 个协调者。

故障时期的处理

前面描述都的都正常情况,如果协调者或者参与者一个或者全部发生宕机时候,2pc 的处理方式在某些时候是无法保证数据一致性。

协调者和参与者会在 ① ② ③ ④ 的前后阶段,同时发生故障,也有可能其中一方发生故障。

① 之前发生故障,即操作还没开始:

  • 协调者宕机:重新选举出一个协调者继续操作,这个宕机的节点重启后询问新的协调者继续操作
  • 参与者宕机:重启后像协调者询问操作,然后继续即可
  • 协调者和部分参与者宕机:同协调者宕机

    ① 之后 ② 之前发生故障,即接收到是否可以 commit 询问,响应之前:

  • 未执行操作前(执行过程中中断):

    • 协调者宕机:询问已经发出,重新选择一个新的协调者,根据参与者返结果继续操作
    • 参与者宕机:等待恢复后询问协调者节点状态,然后继续操作
    • 协调者和部分参与者宕机:重新选择一个新的协调者,根据参与者返结果继续操作
  • 操作执行完成之后:
    • 协调者宕机:重新选举一个新协调者,询问参与者当前步骤
    • 部分参与者宕机:重启后 rollback,询问协调者后续步骤
    • 协调者和部分参与者宕机:选举出新的协调者,重启后 rollback ,询问新协调者后续步骤

② 之后 ③ 之前发生故障:

  • 未接到参与者响应(执行过程中中断):
    • 协调者宕机:选出先的协调者,询问各个节点状态,宕机节点重启后 rollback,询问新协调者后续操作
    • 部分参与者宕机:重启后 rollback 然后询问协调者状态,继续操作
    • 协调者和部分参与者宕机:同协调者宕机
  • 操作执行完成之后:
    • 协调者宕机:选出新的协调者,询问各个节点状态,宕机节点重启后 rollback,询问新协调者后续操作
    • 部分参与者宕机:重启后 rollback 然后询问协调者状态,继续操作
    • 协调者和部分参与者宕机:同协调者宕机

③ 之后 ④ 之前故障:

  • 未接到协调者发出的 commit 或者 rollback:
    • 协调者宕机:指令已经发出,选举一个新的协调者询问正常参与者执行情况。宕机节点重启后 rollback,然后询问新的协调者指令情况,然后继续操作
    • 部分参与者宕机:重启 rollback 后询问协调者,继续操作即可
    • 协调者和部分参与者宕机:同协调者宕机
  • 接受到 commit 或者 rollback(或者执行过程中中断):
    • 协调者宕机:重启 rollback 后询问新协调者,继续操作即可
    • 部分参与者宕机:重启后先 rollback,然后询问协调者操作
    • 协调者和部分参与者宕机:这个时候存在一个极端情况,发出 commit 和接收到的参与者同时宕机,剩下的参与者 timeout 触发 rollback。重启后的宕机机器 commit 已经完成,这时候就和剩下的节点发生了数据不一致的场景,已经完成 commit 的节点也无法 rollback

从故障时期的处理可以总结出,如果是单是协调者发生故障,那么重新选举出一个新的,然后询问参与者的状态即可。如果是单是参与者发生故障,重启后询问协调者操作也可以恢复状态。协调者和参与者同时宕机,同时又在 commit phase 执行完成之后,就会发生数据不一致的场景,这个是 2pc 无法解决的。

2pc 存在的缺点

2pc 组要存在 4 个缺点:

  • 同步阻塞:在 commit phase 阶段完成之前,这部分资源无法被其他程序获取
  • 单点问题:commit phase 阶段协调者发生故障,只能全体 rollback
  • 数据不一致:极端情况会导致部分 commit 部分 rollback
  • 缺乏容错机制:发生无法恢复的错误只能依赖 timeout 执行 rollback