周末晚上,正在家里面看综艺节目,忽然女友跑过来找我打《王者荣耀》。算法
打了几把游戏,终于能够歇息一会了,准备继续看个人综艺,但是女友过来找我给他讲讲到底什么是二阶段提交。网络
还好咱们以前专门给女友介绍过什么是分布式,要否则这个话题说来就话长了。分布式
在以前介绍分布式的时候,咱们以饭店的后厨为例,今天继续以前的例子来讲说什么是分布式一致性。网站
随着饭店的发展,慢慢的从只有一个厨师演变成有多个厨师,进而演变成有洗菜工、配菜师、厨师等多个职位。3d
当有了多种分工以后,就势必须要协调这些人之间的合做。日志
好比餐厅客人点了一份番茄炒蛋,而后后厨开始准备起来,洗菜工开始洗西红柿,配菜师开始准备鸡蛋,厨师开始向锅内加油准备炒菜。这是一种很正常的状况。cdn
可是,若是消息传达的不到位,或者洗菜师傅临时不在厨房等,就会致使有的人已经开始准备起来,可是有的人并无准备。blog
这就像是一个分布式系统同样的,当咱们在电商网站下单的时候,须要有多个分布式服务同时服务,如支付系统进行支付、红包系统进行红包扣减、库存系统扣减库存、物流系统更新物流信息等。游戏
可是,若是其中某一个系统在执行过程当中失败了,或者因为网络缘由没有收到请求,那么,整个系统可能就有不一致的现象了,即:付了钱,扣了红包,可是库存没有扣减。事务
这就是所谓的分布式系统的数据一致性问题。
之因此刚刚的例子中会出现一致性问题,就是由于每个员工都只关注本身所作的事情,没法关注到其余人,那么,要想保证总体的一致性,就须要在后厨中引入一个新的角色,负责统筹,这个角色来进行协调和调配全部人。
那么,引入一个协调者负责协调全部参与者的工做,这个在分布式系统中其实就是X/Open组织定义的分布式事务处理模型,而二阶段提交就是根据这一模型衍生出来的。
举个例子,五我的相约打王者荣耀,想要一块儿玩须要如下几个步骤:
有一我的想要五黑玩王者荣耀,因而他开始联系本身的小伙伴们。
组织者:小A,咱们准备玩王者荣耀,你要是能够来参加的话,如今你就登陆游戏,而后在游戏好友上给我回复个消息。
小A登陆本身的游戏帐号,而后告诉组织者:小A已就位。
组织者:小B、小C、小D,咱们准备玩王者荣耀,你要是能够来参加的话,如今你就登陆游戏,而后在游戏好友上给我回复个消息。
小B、小C、小D分别登陆本身的游戏帐号,而后告诉组织者:小B、小C、小D已就位。
组织者发现全部人都就位了,因而在游戏上逐一通知你们,
组织者:小A,我邀请你了,你进来吧。
小A接受邀请
组织者:小B、小C、小D,我邀请你了,你进来吧。
小小B、小C、小D接收邀请
因而,5我的在王者峡谷愉快的玩耍了起来。
对于五我的开黑这个事务操做,在开始准备前五我的都是空闲状态,忙着本身的事情。在组织者协调过以后,你们也要达成一个一致的状态,即如下两种状况之一:
一、五我的愉快的开始一块儿玩游戏
二、五我的都退出游戏,仍是去忙本身的事情。
若是最后有一部分人在游戏里一直等,另一部分并无进入游戏,那么就是数据不一致了。
以上过程,就是一个典型的二阶段提交(2PC)的过程,在分布式系统中,也有一样的问题,而且能够采用一样的解决办法。
在分布式系统中,每一个节点虽然能够知晓本身的操做时成功或者失败,却没法知道其余节点的操做的成功或失败(只知道本身有时间能够玩王者荣耀,不知道其余人有没有)。
当一个事务跨越多个节点时,为了保持事务的ACID特性,须要引入一个做为协调者的组件来统一掌控全部参与者的操做结果并最终指示这些节点是否要把操做结果进行真正的提交(组织者通知各位参与者一块儿进入游戏房间)。
所以,二阶段提交的算法思路能够归纳为:参与者将操做成败通知协调者,再由协调者根据全部参与者的反馈情报决定各参与者是否要提交操做仍是停止操做。
所谓的两个阶段是指:第一阶段:准备阶段(投票阶段)和第二阶段:提交阶段(执行阶段)
事务协调者给每一个参与者发送Prepare消息,每一个参与者要么直接返回失败(告知组织者本身没时间,不能一块儿玩游戏),要么在本地执行事务(登陆王者荣耀),但不提交(先不开始游戏)。
能够进一步将准备阶段分为如下三个步骤:
1)协调者节点向全部参与者节点询问是否能够执行提交操做,并开始等待各参与者节点的响应。(询问是否能够一块儿玩游戏)
2)参与者节点执行询问发起为止的全部事务操做,并将Undo信息和Redo信息写入日志。(登陆王者荣耀游戏)
3)各参与者节点响应协调者节点发起的询问。若是参与者节点的事务操做实际执行成功,则它返回一个”赞成”消息;(告知组织者本身已经登陆成功)若是参与者节点的事务操做实际执行失败,则它返回一个”停止”消息。(告知组织者本身暂时没法一块儿玩游戏,如本身的帐号被限制没法打排位)
若是协调者收到了参与者的失败消息或者超时(有人不能一块儿玩游戏,或者一直没有回复),直接给每一个参与者发送回滚消息(告知其余人,暂时取消游戏);不然,发送提交消息(邀请你们进入游戏房间);参与者根据协调者的指令执行提交或者回滚操做(进入房间一块儿玩游戏或者退出游戏去作别的事情)。
接下来分两种状况分别讨论提交阶段的过程。
当协调者节点从全部参与者节点得到的相应消息都为”赞成”时:
1)协调者节点向全部参与者节点发出”正式提交”的请求(要求全部已登陆的朋友加入游戏房间)。
2)参与者节点正式完成操做,并释放在整个事务期间内占用的资源(接受邀请,进入房间)。
3)参与者节点向协调者节点发送”完成”消息(点击"准备",进入准备状态)。
4)协调者节点受到全部参与者节点反馈的”完成”消息后,完成事务(进入王者峡谷)。
若是任一参与者节点在第一阶段返回的响应消息为”停止”,或者 协调者节点在第一阶段的询问超时以前没法获取全部参与者节点的响应消息时:
1)协调者节点向全部参与者节点发出”回滚操做”的请求(告知全部人取消游戏)。
2)参与者节点利用以前写入的Undo信息执行回滚,并释放在整个事务期间内占用的资源(退出游戏,去作本身的事情)。
3)参与者节点向协调者节点发送”回滚完成”消息(告诉组织者本身知道了,后面有机会再玩)。
4)协调者节点受到全部参与者节点反馈的”回滚完成”消息后,取消事务(取消本次游戏活动)。
以上过程实际上是有一些缺点的,如
一、当参与者收到组织者的消息以后,须要登陆游戏,在游戏中等待组织者的再次邀请,这个过程比较浪费时间。
二、若是在这个过程当中,组织者忽然有什么事情被打断了,那么那些已经进入游戏的参与者就可能一直等下去。
三、在全部人都登陆游戏以后,组织者经过邀请要求全部人加入他的房间,这时候若是有一些网络异常、或者参与者没在手机前面等状况,可能会有一部分用户加入了房间,有一部分没加入。
四、若是组织者在游戏中开始邀请全部参与者的时候,他邀请了第一我的以后,他和这个被他邀请的人都掉线了。这时候另外三我的就不知道到底应该怎么办了。
以上问题,分布式系统的2PC阶段同样存在,分别对应如下问题:
执行过程当中,全部参与节点都是事务阻塞型的。当参与者占有公共资源时,其余第三方节点访问公共资源不得不处于阻塞状态。
因为协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤为在第二阶段,协调者发生故障,那么全部的参与者还都处于锁定事务资源的状态中,而没法继续完成事务操做。(若是是协调者挂掉,能够从新选举一个协调者,可是没法解决由于协调者宕机致使的参与者处于阻塞状态的问题)
在二阶段提交的阶段二中,当协调者向参与者发送commit请求以后,发生了局部网络异常或者在发送commit请求过程当中协调者发生了故障,这回致使只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求以后就会执行commit操做。可是其余部分未接到commit请求的机器则没法执行事务提交。因而整个分布式系统便出现了数据部一致性的现象。
协调者再发出commit消息以后宕机,而惟一接收到这条消息的参与者同时也宕机了。那么即便协调者经过选举协议产生了新的协调者,这条事务的状态也是不肯定的,没人知道事务是否被已经提交。
总结一下,就是说2PC并非完美的,他存在着同步阻塞问题、单点故障问题、没法100%保证数据一致性等问题。