本质上这是一个分布式环境下关于原子性的协议。也就是 N 个参与者对同一事务:要么都成功要么都失败。
理解它的关键是以“投票”模型来思考,这里的“票”仅仅是:参与者表示自己是否可以完成本次事务。2PC 的两个阶段:
[ol]
投票阶段: 协调者向所有参与者分发投票请求。 参与者各自将投票结果上报给协调者(等同于告知协调者:自己(可以/不可以)完成本次事务)
广播并执行投票意志阶段:协调者拿到所有参与者的投票后,开始向所有参与者广播投票结果,参与者根据投票结果来执行 commit or abort 。
[/ol]
问题的关键是:在投票阶段,有参与者的投票丢了怎么办?即:协调者还未收到所有参与者的投票就挂了,期间参与者也挂了。协调者恢复正常(或者重新选举)后,它还是得死等:挂掉的参与者重新恢复正常,再次问询它的投票是 yes or no ?得到回复后协调者才可以决定继续向前(commit)或者后退(abort)。它不能贸然进入第二阶段,告诉其他参与者投票结果是:abort or commit ,因为挂掉的参与者可能已经发出了 yes 的投票,也可能发出了 no 的投票,只不过他俩在次期间挂了,票丢在了半路上。
丢票后必须死等参与者恢复,这是 2PC 被称为阻塞协议的原因。
3PC 是通过引入临时状态寄存票选结果+超时,来解决阻塞问题的:
在投票阶段达成一致的 yes 之后,再插入一个阶段: 去中心化投票结果阶段
该阶段协调者向所有参与者分发一阶段的最终投票结果,参与者收到后会将其保存下来,并回复协调者:已保存。
这一阶段使得所有参与者手中都有了一阶段最终投票结果,而不再是仅仅在协调者手中。
所以:
无论在哪个阶段,挂掉的协调者恢复(或重新选举)后,都可以根据之前的投票结果继续做出决策,而不是阻塞:
[ol]
[/ol]
而挂掉的参与者恢复后会向协调者或者其他参与者询问:这个事务在一阶段投票的最终结果是 yes 还是 no ? (优先询问协调者,如果协调者挂了,就询问其他参与者。注意:协调者如果超时不能恢复也会被重新选举,所以参与者总是能得到回复)。
回复只有 3 个结果:
[ol]
yes 。于是 commit 事务。该回复如果是来自其他参与者,那原因就是:其他参与者至少有一个已经进入了第二、第三阶段。
no 。 该回复如果来自其他参与者,那原因就是:有其他参与者已经收到协调者的 abort 请求。
未知。 原因是:协调者挂了,其他参与者都处于一阶段,(或者也都挂了)。这种情况下安静等待协调者恢复(或者重新选举)后,做出的决策。协调者恢复后,会向所有参与者继续分发一阶段投票结果。
[/ol]
所以 3PC 不在阻塞的关键就是:
引入中间阶段,在完成去中心化投票结果之前,可以放心 abort 。在去中心化投票结果完成之后,才去请求所有参与者执行投票意志,期间发生意外的的选手在恢复后可以问询其他选手投票结果。
另外:
以上像 保存票选结果 这类描述只是为了好理解,不涉及具体实现细节。