两阶段提交协议
在分布式系统中每个节点都可以知道自己的操作是成功还是失败,但是无法知道其他节点的状态。为了保证一个事务的 ACID 特性,一个节点发生失败就要在所有节点上执行 rollback 操作。需要引入一个 协调者 来维护各个 参与者 的状态,以保证最终一致。
2pc 并不是万能的,需要满足一定的条件才可以使用:
- 一个节点是协调者,其他节点作为参与者,相互之前可以通信
- 每个节点要有 redo log 机制,而且存在持久化存储中
- 节点不会永久损坏,一定时间会重启恢复
Tow-phase Commit Protocol
首先引入几个概念:
- 协调者:维护所有节点
- 参与者:执行具体操作的节点
- prepare phase:准备阶段,写入日志,资源加锁
- commit phase:执行阶段,根据协调者指令执行,资源解锁
上图中 ① 和 ② 表示 prepare phase,③ 和 ④ 表示 commit phase。
在 prepare phase 阶段,协调者发出信息让参与者准备。参与者接受到信息以后一般会做两件事情:
- 根据需要执行的操作生成 redo 日志,用于后续 commit 或者 rollback
- 给所需的资源上锁,防止其他程序获取
参与者完成这两个操作会把结果通知协调者。
当协调者接受到参与者在 prepare phase 阶段的响应(无论 Yes 还是 No),就会进入 commit phase 阶段。在该阶段,如果接受到 prepare phase 的响应所有都是 Yes 时候,会发出 Commit 指令;只要收到一个 No,发出的就是 Rollback 指令。
参与者接受到协调者发出的 Commit 或者 Rollback 指令后会做两件事情:
- 执行 Commit 或者 Rollback
- 清理资源,解除锁的占用
的当这两部都完成以后,会回复 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