大多数数据库事务都具有 ACID 性质,在单机系统中可以帮助我们轻松地进行并发编程,但分布式的全局事务不具有 ACID 性质。而 2PC 和 3PC 就是实现分布式事务的两种协议

Two-Phase Commit (2PC)

「Two-Phase Commit」称为二阶段提交,即事务的提交分为两个阶段: 准备阶段(prepare phase)提交阶段(commit phase) 。其中有 协调者(coordinator)参与者(participant) 两个角色,coordinator 负责调度多个 participant 的行为,并决定 participant 是否进行事务提交。

2PC 的过程可以类比投票,且所有人都有一票否决权,全员通过时才可以进行事务提交。

prepare phase

  1. coordinator 向所有 participant 发送一个消息询问是否可以提交,并等待所有 participant 回复
  2. participant 执行事务,直到被要求提交。并将 undo 和 redo 写入事务日志
  3. 如果 participant 执行成功,则向 coordinator 发送 agreement message(同意提交),否则发送 abort message(否决提交)

commit phase

在 commit 阶段,有成功和失败两种情况:

成功:
如果 coordinator 接收到所有 participant 的 agreement message,就会提交事务

  1. coordinator 向所有 participant 发送提交信息
  2. participant 完成操作,并释放事务期间的锁和其它资源
  3. 所有 coordinator 向 participant 发送确认(acknowledgement,ACK)消息,确认完成
  4. coordinator 在收到所有 participant 的 ACK 消息后,完成事务

失败:
如果有任何一个 participant 发送了 abort message(否决)或者 coordinator 响应超时,事务就会中断

  1. coordinator 向所有 participant 发送回滚消息
  2. 所有 participant 根据 undo 日志来撤销事务,并释放事务期间的锁和其它资源
  3. 所有 coordinator 向 participant 发送 ACK 消息,确认撤销完成
  4. coordinator 在收到所有 participant 的 ACK 消息后撤销事务
Coordinator                                          Participant
                             QUERY TO COMMIT
                 -------------------------------->
                             VOTE YES/NO             prepare*/abort*
                 <-------------------------------
commit*/abort*               COMMIT/ROLLBACK
                 -------------------------------->
                             ACKNOWLEDGMENT          commit*/abort*
                 <--------------------------------  
end

2PC 的缺点

2PC 最大的问题是它是阻塞的,当等待回复过程中另一方宕机时,就会陷入长时间等待,同时 coordinator 只有一个,存在单点故障问题(可靠性低),而且在 commit phase 中由于故障可能导致部分消息发送/接收失败从而丢失一致性(无法保证一致性)。

Three-Phase Commit(3PC)

3PC 是 2PC 的改进算法, 3PC 是非阻塞的。通过拆分 commit phases 引入 prepared commit phase(预提交阶段),重复确认是否提交,解决了 2PC 的部分问题。

考虑一个情况:

  1. coordinator 在 perpare phase 收到一个 agreement,一个 abort
  2. coordinator 向所有 participant 发送回滚消息
  3. 在向第一台 participant 发送回滚消息后,这台 participant 和 coordinator 同时宕机

如果是 fail-stop(持续性宕机) 的情况。在 2PC 中,participant 不知道 coordinator 已经宕机,会长时间阻塞等待。同时就算选举出了新的 coordinator,也无法得知先前的决定是 commit 还是 callback。

而在 3PC 中,在 pre commmit phase 发生这个问题时,剩下 participant 可以重新选举 coordinator 重新协调过程或直接回滚。当通过 pre commit phase 到达 commit phase 时出现这个问题,因为已经 prepared commit 了,重新选举 coordinator 后就可以直接 commit。

3PC 的缺点

虽然 3PC 可以处理 coordinator 和 participant 的 fail-stop,但无法处理在分布式情况中更常见的 fail-recover(宕机一段时间后恢复) 的情况。假设 coordinator 在收到 pre commit 前宕机,此时 participant 选举出新 coordinator 并进行 commit,而旧 coordinator 恢复并且因为没有收到 pre commit 而进行 callback。便导致了数据不一致

还有另一个更简单的场景,当网络出现分区,participant 被分成两个区域,一个区域的收到了 pre commit,一个区域的没有。这时候它们各自重新选举 coordinator,执行了不同的操作,也导致了数据不一致

总结

无论 2PC 还是 3PC 都无法彻底解决分布式系统中的一致性问题,下一篇文章会介绍 Paxos 算法,Paxos 是公认的晦涩难懂,但也几乎是分布式一致性算法的代名词