关于分布式一致性问题不做解释,在长期探索过程中,有几种代表性的解决方案,在这主要说2PC,3PC,Paxos,ZAB。
2PC(Two-Phase Commit),二阶段提交。目前,绝大多数关系型数据库使用此协议完成分布式事务处理。
协议说明
二阶段提交协议是将事务提交过程分为两个阶段进行处理,其执行流程如下:
阶段一:提交事务请求(投票)
1.事务询问:协调者向所有参与者发送事务内容,询问是否可以执行事务提交操作,并等待响应
2.执行事务:参与者执行事务操作,并将Undo和Redo记入事务日志中
3.各参与者反馈结果:将执行结果提交给协调者(YES/NO)
阶段二:执行事务提交
协调者根据各参与者的反馈决定最终是否进行事务提交。
A.事务提交
所有参与者返回YES,执行事务提交
1.发送提交请求:协调者向所有参与者发出commit请求
2.事务提交:参与者收到commit请求后,正式执行事务提交操作,完成后释放资源
3.反馈结果:参与者完成事务提交后,向协调者发送Ack消息
4.完成事务:协调者收到所有参与者反馈的Ack消息后,完成事务提交
B.中断事务
有参与者返回NO,或等待结果超时后,中断事务
1.发送回滚请求:协调者向所有参与者发出Rollback请求
2.事务回滚:参与者收到Rollback请求后,利用Undo信息执行回滚操作,完成后释放资源
3.反馈结果:参与者完成事务回滚后,向协调者发送Ack消息
4.中断事务:协调者收到所有参与者反馈的Ack消息后,完成事务中断
优点
原理简单,实现方便
缺点
1.同步阻塞:执行过程中,所有参与该事务操作的逻辑处于阻塞状态,无法执行其他任务操作
2.单点问题:协调者出现问题,流程将无法流转。当在阶段二出现问题,会让其他参与者处于锁定状态,无法继续完成事务操作。如果提交事务时出现问题,只有部分参与者收到commit请求,导致数据不一致
3.脑裂:由于网络分区,某些参与者未收到commit请求,导致数据不一致。
4.太过保守:任一节点失败导致整个事务失败
3PC(Three-Phase Commit),三阶段提交。基于2PC的问题,进行了改进形成了3PC协议。
协议说明
三阶段提交协议将2PC的提交事务请求阶段一分为二,形成CanCommit,PreCommit,doCommit三个阶段。
阶段一:CanCommit
1.事务询问:协调者向所有参与者发送包含事务内容的CanCommit请求,询问是否可以执行事务提交操作,并等待响应
2.各参与者响应:参与者响应CanCommit请求(YES【进入预备状态】/NO)
阶段二:PreCommit
协调者根据各参与者的反馈决定是否进行PreCommit操作。
A.事务预提交
所有参与者返回YES,执行事务预提交
1.发送预提交请求:协调者向所有参与者发出PreCommit请求,进入Prepared阶段
2.事务预提交:参与者收到PreCommit请求后,执行事务操作,并将Undo和Redo记入事务日志中
3.反馈执行结果:若成功执行事务,则反馈给协调者Ack响应,同时等待最终指令(commit/abort【中断】)
B.中断事务
有参与者返回NO,或等待结果超时后,中断事务
1.发送中断请求:协调者向所有参与者发出abort请求
2.中断事务:参与者收到abort请求或等待协调者请求超时,将中断事务。
3.反馈结果:参与者完成事务回滚后,向协调者发送Ack消息
4.中断事务:协调者收到所有参与者反馈的Ack消息后,完成事务中断
阶段三:doCommit
进行真正的事务提交。
A.事务提交
所有参与者返回YES,执行事务提交
1.发送提交请求:协调者向所有参与者发出doCommit请求
2.事务提交:参与者收到doCommit请求后,正式执行事务提交,完成后释放资源
3.反馈提交结果:参与者完成事务提交后,想协调者反馈Ack响应
4.完成事务:协调者收到所有参与者反馈的Ack消息后,完成事务
B.中断事务
有参与者返回NO,或等待结果超时后,中断事务
1.发送中断请求:协调者向所有参与者发出abort请求
2.中断事务:参与者收到abort请求后,利用Undo信息执行事务回滚操作,完成后释放资源
3.反馈结果:参与者完成事务回滚后,向协调者发送Ack消息
4.中断事务:协调者收到所有参与者反馈的Ack消息后,完成事务中断
一旦进入阶段三,当协调者出现问题或协调者和参与者网络出现问题都会导致参与者在超时后,进行事务提交
优点
降低阻塞范围,出现单点故障后继续达成一致(进入阶段三超时提交)
缺点
引入了新问题:第三阶段网络分区无法通信时,强制提交,可能导致数据不一致问题
Paxos 是一种提高分布式系统容错性的一致性算法,用于构建一个分布式一致性的状态机系统。
协议说明
在该算法中有以下三种角色:Proposer(提议者),Acceptor(协调者),Learner(接纳者)(注意:每个进程不止充当一种角色)。整个算法分为两步
阶段一:Prepare阶段
1.Proposer发送Prepare请求:Proposer给所有Acceptor发送Prepare Request,请求中只携带Proposal ID(全局唯一且递增的提案ID)无具体内容。
2.Acceptor响应:Acceptor收到Prepare Request之后,检查自己之前是否接受过大于等于该提案ID的请求,如果有,则返回该ProposalId;如果没有,则返回空值(表示接受该请求)
Acceptor要求:a.必须响应接收到的第一个提案(防止无提案被响应)
b.不响应proposalId小于等于(<=)当前请求的Prepare Request
c.不响应proposalId小于(<)当前请求的Accept Request
阶段二:Accept阶段
Proposer根据阶段一的结果决定是否到阶段二。如果未收到多数Acceptor的响应,此次Paxos实例结束。如果收到了多数Acceptor节点应答的PrepareResponse,则会进入Accept阶段。
1.Proposer发送Accept请求:Acceptor接收该请求的提案,并将提案通知到所有的Learner
注意:
两阶段的作用
为了解决冲突。Prepare阶段是为了拿到授权,保证最终的提案只有一个。
优点
解决了脑裂、无限等待等问题
缺点
Zab协议全称Zookeeper Atomic Broadcast(Zookeeper 原子广播协议),是为Zookeeper专门设计的一种支持崩溃恢复的原子广播协议 ,是Zookeeper保证数据一致性的核心算法。
Zab借鉴了Paxos算法,但又与Paxos不同,是用于构建一个高可用的分布式数据主备系统
ZAB协议中有Leader和Follower两种角色,整个集群中只有一个Leader,剩下的都是Follower,所有的事务请求都交给Leader进行处理(具体处理过程见消息广播部分)。
ZAB协议中,每一个进程可能处于以下四 种状态之一。LOOKING:处于Leader选举阶段,该阶段无Leader;FOLLOWING:跟随者状态,该服务器是Follower;LEADING:领导者状态,该服务器是Leader;OBSERVING:观察者状态,该服务器是Observer;
ZAB协议中主要有两种模式:崩溃恢复和消息广播。当整个服务无Leader(刚启动、Leader服务器出现异常)时,会进入崩溃恢复模式;当有过半Follower服务器和Leader完成同步,会进入消息广播模式。
消息广播
ZAB的消息广播过程使用的是一个原子广播协议,类似于二阶段提交过程,但是移除了中断逻辑。具体处理流程如下:
1.客户端发起一个写操作请求(Follower收到该请求会转交给Leader)。
2.Leader 服务器将客户端的请求转化为事务 Proposal 提案,同时生成一个全局的ID,即ZXID。
3.Leader 服务器为将Proposal发送给所有Follower。
4.Follower 接收到 Proposal 后,先将其以事务日志的方式写入本地磁盘中,成功后向 Leader 反馈一个 Ack 响应消息。
5.Leader 接收到超过半数以上 Follower 的 Ack 响应消息后,向所有Follower发送 commit 请求,同时完成事务提交。Follower 接收到 commit 消息后,会将上一条事务提交。
一个提案表示为
ZXID:64位二进制数字,其中高32位为epcoh编号(代表Leader周期,每进行一次Leader选举都会+1);低32位可看做为简单的单调递增计数器,每进行一次Leader选取都会重置为0。
如何保证有序(事务可能有先后顺序要求)?
1.zxid全局唯一且递增
2.Leader服务器中为每个Follower维护了一个单独的队列,消息放入队列中,根据FIFO策略异步发送
在这种模式下,一旦Leader服务器出现问题,会带来数据不一致问题,故有崩溃恢复模式来解决这个问题。
崩溃恢复
当在消息广播过程中,Leader崩溃或与过半的Follower失去联系,将会进入崩溃恢复模式,该模式主要有两部分:Leader选举和数据恢复
Leader选举
Leader选举算法必须满足以下两个要求:
1.确保已经在Leader服务器上提交的事务最终被所有服务器都提交
事务已经得到过半Follwer的反馈且在Leader上提交,在commit消息发送完前崩溃。此种事务最终提交
2.确保丢弃哪些只在Leader上服务器上被提出的事务
事务仅在Leader上提出,在发送过程中崩溃。此种事务被抛弃
故选举算法要求选举的Leader服务器拥有集群内最高ZXID的Proposal,这就保证新的Leader拥有所有已提交的提案。并且可以省去Leader服务器检查Proposal的提交和丢弃步骤。
下述为集群刚启动时的投票流程
1.每个Server发出一个投票(投自己)
该投票包含myid(服务器id编号,在config中配置)和ZXID,我们用(myid,ZXID)表示。
2.接收来自各个服务器的投票
每个服务器会接收来自其他服务器的投票,检查其有效性(是否是本轮投票,是否来自LOOKING服务器)。
3.处理投票
收到投票后,针对每一个投票和自己投票进行PK,然后将PK胜出的票选再次发出,PK规则如下:
a.优先比较ZXID,ZXID较大的票获胜
b.ZXID一样,比较myid,myid大的获胜
4.统计投票
每次投票后,服务器会统计所有投票,判断是否有过半机器收到相同的投票信息
5.改变服务器状态
根据投票的统计结果,确定服务器状态,然后各服务器变更为对应状态。
服务器运行过程中需要进行Leader选举,算法与上述类似,区别在于:
1.先将服务器(Observer不参与)状态变更为LOOKING
2.投票中的ZXID为当前服务器实际ZXID
Zookeeper高版本中只使用了TCP版本的FastLeaderElection算法。该算法流程就是上述流程,下面对该算法的部分细节进行描述:
投票实体(Vote)设计
id:被推举的Leader的SID值(即上述myid)
zxid:被推举的Leader的ZXID值
electionEpoch:逻辑时钟,几轮投票轮次,每进入新一轮投票都会+1
peerEpoch:被推举Leader的epoch
state:当前服务器状态
TCP连接
为了避免大量重复连接,设计了如下规则:
只允许SID大的服务器主动和其他服务器建立连接,否则断开连接。
消息队列
QuorumCnxManager内部维护了一系列的队列,用于保存消息。其中每个队列按照SID分组形成队列集合,
选举轮次
在该算法中,有logicalclock属性,记录选举轮次。新一轮的投票开始时,logicalclock会进行自增
数据恢复
当完成Leader选举后,将进入数据恢复阶段。此阶段流程如下
1.Leader将自己的事务作为初始化事务集Ie
2.Leader将epcoh和Ie以NEWLEADER(epcoh,Ie)的形式发送给所有Follower
3.Follower收到NEWLEADER(epcoh,Ie)消息后,与自己的epcoh(记为e0)进行对比,有以下两种情形
a.e0