2PC

2PC是分布式系统中最简单的一致性协议,其实质是通过把事务提交分成预提交和确认提交两个阶段,来给所有参与者一个表态(赞成或者反对)的机会。

在两阶段提交的过程中有两个重要的时间节点:一个是参与者投票。每个参与者在作出决定之后,这个决定是无法自己单方面更改和撤销的;另一个是协调者决定。协调者根据参与者反馈作出全局决定,该决定被持久化到日志之后也无法变更。

这个协议最让人诟病的地方在于它是一个阻塞式的协议,体现在:

  • 若协调者发送完预提交请求之后出现故障,所有的参与者在执行完事务后都会阻塞。由于参与者无法提交或者退出事务,导致自己占有的本地资源无法释放,影响本地其它事务执行。
  • 若参与者在投赞成票之后出现故障(投反对票可以单方面退出),协调者无法将决定告知该参与者。为了保证数据的强一致性,协调者必须一直重试提交直到参与者应答为止。

上述缺点在大型分布式系统中无法容忍,因为随着集群规模增大,节点故障几乎是必然事件。

3PC

针对2PC的不足,论文A Formal Model of Crash Recovery in a Distributed System提出了3PC的初始模型,可以看作是2PC的升级版。3PC协议有两个重要改进,一是将2PC的commit阶段进一步分成pre-commit和do-commit两步,二是引入超时机制。这两个改进提升了系统的容错性。

3PC中若协调者在第一阶段发送完can-commit请求之后发生故障,所有参与者都会因为超时退出事务,避免了阻塞;若协调者在发送完pre-commit请求后发生故障,参与者则会在等待do-commit上超时后提交事务。这里之所以能安全提交,关键在于参与者已经处于pre-commit状态(即收到并处理了协调者的pre-commit请求),这说明所有参与者都已经在can-commit阶段投票赞成。回过头看2PC,如果第二阶段收不到commit请求,参与者此时既不能提交也不能退出事务,因为它无法得知协调者作出的全局决定。这也是为什么即使2PC引入超时机制也没有作用的原因。

在容错方面,无论是协调者还是参与者,节点在恢复之后都需要向其它参与者询问之前的事务状态来保证数据一致性。协调者在恢复后,如果发现有任何参与者已经提交或者退出,自己会相应作出一致的决定;参与者同理。不过要注意的是,即使参与者恢复后发现自己已经处于pre-commit状态也不能就马上提交,仍然需要询问其他参与者事务状态,因为协调者可能在等待自己对pre-commit的确认时超时而让其它参与者退出。

然而,我某天偶然想到这样一个场景:假设有A,B和C三个节点,其中C是协调者,A和B是参与者。C在发送完pre-commit请求给A后还没来得及把相同请求发送给B就停机,协议怎么处理?按照我之前的理解,A收到请求后进入pre-commit状态,等待do-commit超时提交事务;B尚处于can-commit状态,等待pre-commit超时退出事务。C恢复之后询问A和B,A和B都处于最终状态,且状态不一致。

我查了好多资料都没有找到满意答案,直到读到Three-phase commit protocol这篇对3PC协议总结性的文章。虽然文章内容没有直接涉及上述问题,但是我在其参考文献MaBE Agents and n-Phase-Commit里找到了协议的处理方法。

If a participant cannot receive a message in the wait state wi, it asks all re-maining participants if one of them is in PRE-COMMIT state. If there is one, the participant will shift to PRE-COMMIT and finally commit, otherwise it will go to ABORT.

简单来说,参与者B在can-commit状态中等待协调者的pre-commit请求时如果发生超时,并不是简单退出,而是需要查看有没有其它参与者已经处于了pre-commit状态。如果存在至少一个这样的参与者,那么说明协调者C是在发送了部分pre-commit请求之后才停机的,于是为了保证数据一致性自己也提交事务。

看上去3PC比2PC似乎好了不少,但是实际使用仍然存在重大缺陷:

  • 仅容忍单点故障,协议不允许协调者和参与者同时停机。继续沿用上面的例子,C停机后B询问A的状态,A这时也停机,B等待A超时后会陷入两难境地。
  • 同步通信模型。该模型假设网络延迟有上限,不能出现网络分区。3PC的超时机制基于这个假设,一旦超时则表明通信节点出现永久故障。而实际系统一般都只符合部分同步(partially synchronous)假设,通信超时的情况下无法准确分辨到底是网络问题还是节点故障。

总体感觉3PC协议理论价值大于实用价值。