Skip to content

Latest commit

 

History

History
87 lines (56 loc) · 15 KB

consensus.2pc.md

File metadata and controls

87 lines (56 loc) · 15 KB

共识算法之:2PC

引言

在分布式系统发展的过程中,对于如何达成不同副本的数据一致性,诞生了很多不同的算法。其中两阶段提交协议(Two-Phase Commit,2PC)是一个非常基础的算法,这个算法最初是随着分布式数据库而发展起来的。实际上这个算法存在着很多问题,不过透彻的理解这个算法,有助于理解共识算法中的许多基本问题,所以本文着重讨论这个算法。在现实生活中,有很多模型与这个类似,所以在这里就以约饭局这种最常见的情形来引入。

一个人的饭局

这是最简单的情况,我一个人去吃饭,那当然是自己一个人做决定就好了,无论是我想去海底捞,还是来一份兰州拉面,或者是点一份外卖,我自己想吃什么就吃什么,想什么时候吃就什么时候吃,不需要与其他人沟通商量。

对于计算系统来说,独立的一台计算机上的数据,只要自己存储正确了就可以了,不需要与其他计算机同步。

两个人的饭局

在这里我们先做一个前提条件的限定,即人和人之间约饭局的沟通方式只能通过短信沟通。

有两个人要约着一起吃饭,情况变得稍微不一样了。某天上午一上班,小蓝就给自己的死党小明发短信说,中午一起去吃披萨吧。一个小时过去了,小蓝没有收到任何回复短信,他就想,该死的小明,中午到底要不要去吃披萨呢?你要是不去的话早说呀,我就去约小红了。小蓝又给小明发了一条短信,这次很快就收到了小明回复的短信,OK,中午一起去吃披萨。

这个例子中,只有两个人需要通过短信的方式来确认,中午是否一起去吃披萨。由于短信是不可靠的,在发送的过程中有可能丢失,所以在这次约饭局的过程中,小蓝发的第一条短信,小明是否收到了是不确定的。也许小明根本就没有收到第一条短信,或者是小明收到了第一条短信,马上就给小蓝回复了,可是小明回复的短信丢失了,没有正确的发送给小蓝,导致小蓝一个小时都没有收到回复短信。

对于两台计算机的通讯来说,情况也是类似的,而且这是最基础的网络模型。A通过网络给B发送了一条消息,根本无从知晓B是否收到或者是否处理了。需要B给A回复一条Ack消息,告诉A自己这边收到了。但是回复消息也有可能丢失,所以假如B要求A再给B发一条确认收到回复的消息的话,这条消息本身也有可能丢失。假如B为了确认收到再给A回复确认消息的话,,这种确认消息会无休止的循环下去。。。。。。

如果约饭局的人更多会怎么样呢?

三个人的饭局

这时候比两个人约饭局又复杂了一些。小蓝早上同时给两个死党小明和小强发了短信,约他们中午一起去吃披萨。这次小明很快就回了短信,OK。但是不知道什么原因小强却没有回复短信。这个时候小蓝就只有等着,不知道小强那里什么情况。小明这里也纳闷,我都说了要去,到现在还没有消息,中午到底还要不要一起去呢?一个小时之后,小强给小蓝回复了短信,抱歉哈,今天中午领导请大家吃饭,不能和你们一起吃披萨了。小蓝一看,赶紧给小明发短信,今天中午的饭局取消,自己解决吧。

在这个例子中,需要三个人对于中午是否一起去吃披萨达成共识。“中午一起去吃披萨”这个建议由小蓝发起,需要获得小明和小强的认可,中午的饭局才能落实。小明很快就确认了“中午一起吃披萨”这个建议,由于某种未知的原因导致小蓝并没有及时收到小强的回复,从而无法确定小强对这个建议的态度,所以小蓝和小明就只能等着,也不能安排其他的饭局。直到一个小时之后,小强才回复消息,无法参加中午的饭局,小蓝一看,中午的饭局告吹了,只好通知小明,取消饭局。

上述的情况如果极端一点点,小强根本就没有收到小蓝的短信,或者小强下午才看到小蓝早上的短信,匆匆做出回复,但这时候已经太迟了,午餐时间已经过去了,难道小蓝和小明要一直等着小强吗?当然不会。其实小蓝还有两个选择:1,一个小时没有收到小强确认短信的情况下,再次向小强发送短信,问他中午我们仨一起去吃披萨好不好? 2,在早上发送第一条短信的时候,就告诉小明和小强,如果一个小时之内没有收到你们的确认短信,中午的饭局就取消。

对于计算系统来说,三个节点之间确认一个数据和更多节点之间确认一个数据从理论上来看已经没有太大差异了。对于发起建议的节点,专业术语是协调者(Coordinator),对于接收建议的节点,专业术语是参与者(Participant)。在2PC协议中,协调者需要收到所有参与者的确认回复之后,再次通知所有参与者执行建议。只要有一个参与者回复说无法执行这个建议,那么协调者只好通知所有的参与者,取消刚才的建议。这个模型看起来很简单,然而在执行过程中的任意阶段都有可能发生异常,从而导致这个模型无法正常工作。

下面,我们以三个人的饭局和2PC协议做对比的方式来解说2PC协议。

注意:在经典的2PC协议中,并没有设置超时机制,这里之所以加入是因为使用了三个人约饭局这样的日常生活中的例子做对比,而我们日常生活中的例子是经常加入超时机制的。所以为了明确的对比和易于理解,这里加入了超时机制。

三个人的饭局 2PC 解说
Prepare阶段
小蓝早上同时给两个死党小明和小强发了短信,约他们中午一起去吃披萨,并且设定时间,一个小时没有收到你们的答复,就取消饭局 协调者给参与者发送Prepare消息,并且设定一个超时时间 在这里,小蓝是协调者(Coordinator)角色,小明和小强都是参与者(Participant)角色
正常:小明和小强都收到了预约饭局的短信 正常:参与者全部收到了Prepare消息 这是最理想的状况,在现实世界中,这也是绝大多数的情况。异常出现的比例还是很低的
异常:短信信道出错,小蓝这里直接显示短信发不出去 异常:网络出错,协调者发送Prepare消息失败 这种情况是最简单的,发送者自身知道消息没有能够发送出去,过一会儿重新发送一遍就好了。我们的日常生活中就是这么干的
异常:小蓝这里显示短信发送成功,但是小明和小强至少有一个人没有收到短信。假设小明收到了短信,而小强没有收到短信 异常:协调者这里感觉到是消息发送成功,但是参与者并不是全部都收到消息了,至少有一个接收者没有收到消息 对于没有收到消息的接收方来说,根本就不知道有这么一个要求,所以当然不会做出任何反应
反馈
收到小蓝短信的人(小明)给小蓝发送回复短信 收到协调者消息的参与者给协调者发送反馈消息 也只有收到了消息的接收方才能够做出反应
异常:短信信道出错,直接显示发送反馈短信失败 异常:网络出错,直接显示发送反馈消息失败 这种情况也好处理,稍后重试即可
异常:小明这里显示短信发送成功,然而实际上小蓝并没有收到反馈短信 异常:参与者显示消息发送成功,然而实际上协调者并没有收到反馈消息 这个时候就有点儿麻烦了。接收方已经发送反馈了,而且显示成功。但是由于网络原因,协调者并没有收到参与者的反馈?
异常:小明收到了小蓝的短信,还没有来得及回复,就被领导找去开会,讨论事情去了,根本没机会回复短信 异常:参与者收到了协调者的Prepare消息,但是由于同时执行的任务太多,无法及时回复短信。或者宕机而无法回复短信 这种情况的发生也很正常,或许是由于硬件或者软件的故障导致宕机,也或许是当时系统正在执行某种极其耗费系统资源的任务,根本无法及时回复
接收反馈
正常:小蓝很快就收到了小明和小强的反馈短信 正常:协调者在超时时间内收到了所有参与者的反馈消息 这是最理想的情况,也是大多数时候的情况
异常:小蓝的手机没电了,根本没收到小明和小强的反馈短信 异常:协调者自身宕机了,无法收到参与者的反馈消息 对于小蓝来说,手机充电开机之后,还可以收到短信。可是计算机宕机恢复之后,却无法收到之前丢失的消息
异常:一个小时过去了,小蓝没有收到任何人的反馈消息 超时时间已过,协调者没有收到任何参与者的回复 超时时间已过,还有一个可选方案是,再次发送Prepare消息
异常:小蓝只收到了小明的反馈。但是一个小时过去了,还没有收到小强的反馈 异常:在超时时间之内,协调者没有能够收到所有参与者的反馈 没有收到反馈的参与者,可能有几种原因:1,参与者没有收到Prepare消息。2,参与者宕机或者来不及回复。3,回复消息丢失
Commit阶段
正常:小蓝收到了小明和小强的短信,确认都可以参加中午的饭局。所以小蓝给小明和小强发出确认短信,确定了中午一起去吃披萨 正常:协调者收到了所有参与者发回的反馈消息,这个动作可以执行。于是协调者向所有参与者发送了执行动作的Commit消息 这是正常流程,也是我们希望的流程
正常:小蓝收到了小明的反馈短信说可以参加饭局,收到了小强的反馈短信,内容是无法参加中午的饭局。这时候小蓝只好给小明和小强都发送取消饭局的短信 正常:协调者收到了所有参与者的反馈消息,至少有一个参与者反馈说无法执行这个动作。协调者向所有参与者发送Abort消息 协调者只有在收到所有参与者都确认可以执行这个动作的前提下,才能向所有参与者发送Commit消息。只要有一个参与者回复说无法执行,那么整个动作就要取消
异常:小蓝只收到了小明的回复短信说可以参加中午的饭局,一个小时过去了也没有收到小强的回复短信。这时小蓝只好向小明和小强都发送短信说,中午的饭局取消 异常:协调者在超时时间之内没有收到所有参与者的反馈消息,只好向所有参与者发送Abort消息 协调者不能收到所有参与者确认可以执行的反馈,那么就取消这个动作
异常:小蓝被突然发生的事情打断,或者睡着了,不能及时给小明和小强发出确认消息 异常:协调者宕机,不能及时给参与者发送Commit消息 这个情况也比较糟糕。协调者在收到参与者的反馈消息之后,应该立刻把这个消息持久化存储。在宕机恢复之后,再去查看自己的状态信息。不过也有可能是这样,协调者收到了参与者的反馈消息,还没有来得及持久化存储就宕机了。宕机恢复之后也无法知晓收到过参与者的反馈消息,这种情况下,只能当做超时处理了
异常:小蓝向小明和小强都发送了确定中午饭局或者取消饭局的短信,但是显示发送失败。这种情况稍后再重发一遍就好了 异常:协调者向参与者发送执行或者取消指令的时候,网络不通,发送失败。这种情况下,稍后再重新发送指令就好了 很明确是网络不通而发送失败的情况,是比较好处理的,稍后重试吧
异常:小蓝向小明和小强都发送了确定中午饭局或者取消饭局的短信,显示发送成功。但是小明或小强并没有收到这个短信 异常:协调者向所有参与者发送了执行或者取消的消息,但并不是所有参与者都收到了这条消息 这种情况就比较麻烦了。收到消息的参与者很明确的执行了指令,执行或者取消。但是对于没有收到消息的参与者,就无所适从了,只有在超时时间过去之后,自动取消
结果返回
- 参与者执行完成Commit动作之后,向协调者反馈Commit结果 饭局这个例子就不需要再次返回结果了,中午三个人一起吃了披萨就是执行结果,都一起吃饭了,就不需要发短信回复结果了,因为小蓝已经知道结果了。然而计算系统却需要回复结果,不然协调者是不可能知道的

优缺点

  • 优点:原理简单,易于理解,易于实现
  • 缺点:同步阻塞,单点故障,数据不一致
  • 注意:2PC协议本身是没有引入超时机制的
缺点 三个人的饭局 2PC 解说
同步阻塞 在预约饭局的过程中,小蓝、小明、小强三个人,任意一人出现意外情况,比如:睡着了,去开会了等等,其他两人都只能等着,导致整个预约过程无法继续下去 协调者和所有的参与者都必须等待其它节点的响应结果,任意一个节点宕机或者网络故障,都会导致其他节点一直等待 其中一种解决方案就是引入超时机制
单点故障 小蓝是核心角色,如果小蓝睡着了,整个饭局预约工作便无法推进。更为严重的是,如果小蓝是在第二阶段睡着了,小明和小强无法确定,是要一直等着消息,还是可以自己去食堂吃饭 协调者一旦宕机或者协调者的网络断掉,参与者就不知道下一步该怎么办。尤其严重的是,协调者如果在第二阶段发出Commit指令前宕机,参与者还在等待Commit或者Abort指令,没有指令到达,就一直处于资源锁定状态 引入超时机制或许是一种解决办法,但是不完善
数据不一致 在小蓝正式向小明和小强发出确定中午一起吃饭的短信之后,如果只有小明收到了短信,而小强没有收到短信,那么小明会去参加中午的饭局,而小强则不会去 在第二阶段发出Commit或者Abort指令之后,如果只有部分参与者收到了指令,那么收到指令的参与者与没有收到指令的参与者就会采取不同的动作,导致整体数据不一致 这个时候,超时机制也不好使,对于小强来说,他不知道该去参加中午的饭局,还是不该去参加中午的饭局?因为小蓝和小明可能会一起去吃披萨,也有可能不去

小结

2PC协议是经典的分布式系统共识算法,虽然存在着很多固有的缺陷,但却为很多实用的共识算法打下了基础。透彻的理解这个算法,有助于更好的理解那些实用的共识算法,比如:Paxos