关于2PC, 3PC的一点思考

1,327 阅读4分钟

2PC,3PC具体算法这里就不赘述了,搜索引擎上有很多文章详细介绍了算法+博主自己的理解,我在阅读了很多篇之后,终于想通了一些问题,在这里梳理和记录一下。

两阶段提交(2PC)是一种强一致性分布式事务算法,这里的强一致性是一种相对的说法,是相对于柔性事务算法,如TCC,Saga这类满足最终一致性算法而言。2PC基本满足事务的ACID特性。

我为什么强调是相对的强一致,而不是绝对保证一致,因为,2PC(或者3PC)是一种同步阻塞模式,所有节点统一行动听指挥,最大程度避免中间状态的存在(对于最求最终一致性的算法来说,中间状态是被允许的), 在一切顺利,没有错误发生的情况下能够保证一致性,但是如果在某个阶段,协调者挂了,会造成数据不一致现象发生。以下面这张2PC图举例,如果在第二阶段时,协调者给参与者A发送Commit消息之后就宕机了,A会提交事务,而B不会,会一直阻塞住,因此就产生了数据的不一致。

除了数据不一致,2PC还有两个缺点

  1. 同步阻塞问题,在Prepare阶段是需要锁住资源的,因此别的资源管理器要访问该资源时会被阻塞,想象一下要是一个长事务在执行过程中,锁住了太多资源又迟迟不释放,那将对整个系统的性能造成极大影响。与之相比,TCC,Sage这类柔性事务就可以不用锁住资源。
  2. 单点故障,无论是2PC还是3PC,都非常依赖事务协调者的稳定性,2PC尤为明显,因为一旦协调者宕机,参与者只能等待协调者的命令,无法自我恢复,加重了资源阻塞的问题。

3PC的引入是为了缓解以上的一些问题(我这里不认为是能够解决以上问题),3PC引入了参与者的超时机制,以及将原来的Prepare,Commit节点拓展为三个阶段CanCommit, PreCommit, DoCommit。如下所示:

我们来看看3PC是如何缓解以上问题的吧,在2PC中,由于协调者宕机,会导致参与者无休止等待命令从而阻塞资源。在3PC中,由于引入了参与者超时机制,如果发生协调者宕机,参与者会在等待超时后执行回退或者提交指令。参与者超时后怎么判断自己应该回退还是提交呢?根据自己有没有收到PreCommit指令就好了。可以说PreCommit指令是一个信号,也是个barrier,一旦收到这个指令,就算协调者之后宕机,参与者也会自己执行DoCommit。

问题1:在2PC上只引入参与者超时机制,可以达到3PC的效果吗?

如果只在2PC上引入参与者超时机制去试图解决由于协调者宕机导致的参与者阻塞的问题,就意味着某个节点只要完成了Prepare阶段,只要不收到协调者的absort指令,他就会提交。这有什么问题呢?

我们来对比一下3PC,在3PC中,参与者只要完成了PreCommit阶段,只要超时时间内没收到absort指令,就会提交。对于3PC的参与者来说,收到PreCommit指令,不仅仅是被要求执行PreCommit指令,也传达了另一层消息,也就是其余参与者都完成了Prepare阶段,这是个很重要的信息,这个信息确保了全部参与在执行PreCommit之前达到了一致的状态,这样在发生协调者宕机的情况下,大家会相信(只是相信,而不是确定)别的节点也应该会完成DoCommit阶段,于是在等不到DoCommit命令的时候,自己主动完成DoCommit。

回到2PC,如果参与者节点完成Prepare阶段后就超时自动Commit,那么就是参与者在完全不知道其他节点状态的情况下,自己超时就Commit了,这样造成数据不一致的概率会远大于3PC。

结论:只在2PC的基础上引入超时机制是不行的,还需要加一个中间阶段,这个阶段能够让所有参与者完成状态统一,提高事务完成成功率。

问题2:2PC被认为是强一致分布式事务协议,但是却保证不了数据一致性,这是为什么呢?

我认为这里的强一致是一种相对的说法。它要求节点在真正行动之前达到一致的状态以提高事务成功的几率。至于到达一致状态之后,各个节点数据修改与落盘这个过程,没有任何人能保证这个过程中某个节点不出错。