CAP原则
一个分布式系统不可能同时满足一致性,可用性和分区容错性,这三个原则最多只能满足其中的两项。
- 一致性
在分布式环境下,数据副本在多个数据节点中的其中一个节点上成功更新数据后,其余的各个节点应该更新成最新的值,保证所有的用户都读取到最新值 - 可用性
系统提供的服务必须一直处于可用状态,对于用户的每一次操作请求总是能够在有限的时间内返回结果 - 分区容错性
分布式系统在遇到任何网络分区(不同的节点分布在不同的子网络中)的故障时,仍然能保证对外提供一致性和可用性的服务,除非是整个网络环境都发生了故障
BASE理论
- BA:基本可用
- S:软状态(中间状态)
- E:最终一致性
核心思想:即使无法做到强一致性,但是每个应用可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。
一致性协议
1. 2PC算法
二阶段提交协议将事务的提交过程分成两个阶段来完成,即协调者的提交事务请求阶段,参与者的执行事务提交阶段
阶段一,提交事务请求:
-
事务询问
协调者向所有的事务参与者发送事务内容,并询问参与者是否可以进行事务提交操作,然后等待各个参与者的响应 -
执行事务
参与者开始执行事务操作,并将Undo和Redo记录在事务日志中
-
各个参与者向协调者反馈事务询问的结果
如果参与者成功执行了事务操作,则向协调者返回YES作为响应,表示事务可以执行;否则参与者没有成功执行事务操作,返回NO,表示事务不可以执行
阶段二,执行事务提交
-
参与者全部返回YES时:
1.发送提交请求
协调者向所有参与者发送事务的Commit请求
2.执行提交
参与者在收到Commit请求后,会真正执行事务的提交操作, 并在提交完成后释放在整个事务执行期间所占用的各种资源3.反馈事务提交结果
参与者在完成事务提交操作后,向协调者发送一个ACK消息4.完成事务
协调者在收到参与者的反馈消息后,完成此次事务。 -
有任何一个参与者返回NO,或者网络超时,或者协调者无法获取到参与者的反馈时:
1.发送回滚请求
协调者向所有的参与者节点发送Rollback请求2.执行回滚
参与者收到Rollback请求后,会利用在阶段一中记录的Undo日志信息执行事务回滚,在回滚完成是释放事务执行期间所占用的各种资源3.反馈回滚结果
参与者在完成事务回滚以后,向协调者发送一个ACK反馈消息4.中断事务
协调者在收到参与者的反馈消息后,完成此次中断事务。2PC算法的缺点:会导致所有参与者在等待事务提交过程同步阻塞,存在参与者的单点问题,还会存在只有部分参与者收到commit请求导致最终数据不一致
2. 3PC算法
三阶段提交协议是将2PC算法中的阶段一再分成两步来完成
阶段一,CanCommit阶段
- 事务询问,发起“是否可以执行事务提交操作”的请求,并开始等待各个参与者的反馈
- 参与者向协调者反馈YES或者NO
阶段二,PrepareCommit阶段
-
反馈YES,执行事务的预提交:
1.协调者发送事务预提交请求
2.执行事务操作,记录Undo和Redo信息
3.参与者反馈ACK信息
-
反馈NO/网络超时问题,中断事务:
1.协调者发送abort中断请求
2.收到中断请求,或者超时,中断事务
阶段三,doCommit阶段
-
反馈YES,执行提交
1.协调者发送doCommit请求
2.参与者执行提交操作,释放事务资源
3.完成提交后反馈ACK消息给协调者
4.协调者收到各个参与者的ACK消息后,完成事务
-
反馈NO/网络超时问题,中断事务:
1.协调者向所有的参与者节点发送abort请求
2.参与者收到abort请求,或者网络超时后,会利用在阶段二中记录的Undo日志信息执行事务回滚,在回滚完成是释放事务执行期间所占用的各种资源
3.参与者在完成事务回滚以后,向协调者发送一个ACK反馈消息
4.协调者在收到参与者的反馈消息后,完成此次中断事务。
3PC算法优缺点:降低了参与者等待事务提交过程的阻塞范围,解决了协调者的单点问题,但是依旧会存在最终数据不一致问题。
3.Paxos算法
Proposer提案生成算法:
1.proposer选择一个新的编号为M(n)提案,然后向所有Acceptor的其中一个子集合的成员发送请求, 并且要求该子集合中的所有 Acceptor做出如下的响应:
-
向Porposer承诺保证不会在批准编号小于M(n)的提案
-
如果Acceptor之前已经批准过任何提案,则向Proposer返回之前所批准的提案中编号小于M(n)但是编号最大的那个提案的值
此次请求称为编号为M(n)的提案的Prepare请求
2.如果Proposer收到了超过半数以上的Acceptor的响应结果,则他就可以生成编号为M(n),Value值为V(n)的提案, V(n)就是所有响应中编号最大的提案的value值;另外如果半数以上的Acceptor之前没有批准过任何提案,则响应中不包括任何提案,此时V(n)的值可以有Propoeser自己任意选择
在确定提案之后,Proposer会将确定的提案再次发送给某个Acceptor集合,并希望获得批准,称此次请求为Accept请求。
Acceptor批准提案算法:
-
如果是Prepare请求,Acceptor可以在任何时候响应一个Prepare请求
-
如果是Accept请求,Acceptor可以在不违背承诺的前提下,可以响应任何Accept请求
Paxos算法的执行过程:
阶段一:
-
1.Proposer选择编号为M(n)的提案,向所有Acceptor其中某一个超过半数的子集合发送Prepare请求
-
2.如果一个Acceptor收到一个编号为M(n)的提案的Prepare请求,而且M(n)大于该Acceptor之前已经 响应过的所有Prepare请求的编号,那么该Acceptor就将之前批准过的最大编号的提案反馈给Proposer,同时该Acceptor承诺不会再批准编号小于M(n)的任何提案
阶段二:
-
1.如果Proposer收到了超过半数的Acceptor对于其发出的编号为M(n)的提案的请求的反馈,则Proposer就会发送一个针对[M(n),V(n)]提案的Accept请求给Acceptor
-
2.如果Acceptor收到了针对[M(n),V(n)]提案的Accept请求,只有该Acceptor尚未对编号大于M(n)的提案的Prepare请求作出响应,那么该Acceptor就可以通过这个提案
Learner提案的获取:
一个超过半数Acceptor批准的提案,由Acceptor批准后发送给一个特定的Learner集合,该集合中的每一个Learner都可以在一个提案被选定之后同时其他的所有Learner.
Paxos算法活性保证:
为了保证算法陷入死循环,需要选取一个主Proposer,并且只有主Proposer才能提出提案。 这样,只要主Proposer和过半数的Acceptor能进行正常的网络通信,只要主Proposer提出了一个编号更高的提案,那么该提案终将会被批准。
Zookeeper
角色:
- Leader
- Follower
- Observer
ZAB协议:
所有的事务请求都由一个全局唯一的Leader服务器来协调处理,如果事务请求到达其余的Follower服务器,则该服务器会将事务请求重新转发给Leader服务器;然后Leader为事务请求生成一个全局递增且唯一的事务ID并将事务转化成一个事务Proposal(提议),然后将此Proposal通过消息广播分发给各个Follower服务器,在广播的过程中Leader会为每个Follower分配一个FIFO的队列,将消息广播发送到队列中。之后Follwer服务器在接收到消息后首先将事务以事务日志的形式写入到本地磁盘,并且在成功写入后反馈给Leader一个ACK响应,如果不成功则直接丢弃。当Leader服务器在接收到过半数的Follower服务器的ACK响应后,会发出一个Commit广播消息给所有的Follower通知它们提交事务,同时自身也完成事务的提交。
ZAB协议主要包括原子广播和崩溃恢复两个过程,分为三个阶段:发现,同步,广播阶段;
集群中的节点都处于:状态 LOOKING, FOLLOWING, LEADING, OBSERVING中的一种
1.发现
开始选举Leader时,所有处于LOOKING状态的节点都会尝试选举自己为Leader,将自己的serverID、当前的主进程周期epoch,所处理过的事务中最后一个事务表示zxid的值等信息广播给其他所有正常节点,其余的所有节点在收到广播消息后开始执行选举的主要逻辑:
从自己收到的所有的选票中,做选举处理:
-
1.如果某一个节点收到的广播中选举提议的主进程周期值大于自己的当前的主进程周期,则承认此时广播消息中对应的server则为主
-
2.如果主进程周期相等,而且广播消息中的zxid大于当前节点所处理的所有事务的最大ID,则承认此时广播消息中对应的server则为主
-
3.如果前面两个都相等就比较server id,如果比当前的大,则承认此时广播消息中对应的server则为主, 同时更新自己的主进程周期的值,zxid的值,leader的值为消息中相应的值;将选票信息重新更新后,然后向所有发给自己投票的的服务器发出通知:我已经选出了一个Leader。 在选举结束后,最后将自己的状态有LOOKING更新为FOLLOWING
-
4.选举逻辑结束后,follower会给他承认的Leader发送一个ACK反馈,在这个ACK消息中包含了该follower所有处理过的事务提议的集合和最后一个处理过的事务的主进程周期,在准Leader如果收到了过半的服务器的ACK响应,则准Leader就会在所有的ACK中选取一个Follower处理过的事务提议集合作为自己的初始化集合,为同步做准备
2.同步:
进入同步阶段后,Leader会将自己的主进程周期和事务的初始化集合广播到所有的节点,然后每个节点在取得消息后,会和当前自己的主进程周期进行比较,如果相等的话,则会接受Leader的事务列表并处理,然后返回ACK消息;如果主进程周期不相等,则说明当前节点还处在上次某一轮主进程周期中,无法参加本次的同步,然后主动进行新一轮进程周期的循环。如果Leader收到了过半的节点的ACK后,Leader会向所有的节点发出Commit广播消息,完成同步过程。
3.广播:
Leader为事务请求生成一个全局递增且唯一的事务ID并将事务转化成一个事务Proposal(提议),然后将此Proposal通过消息广播分发给各个Follower服务器,在广播的过程中Leader会为每个Follower分配一个FIFO的队列,将消息广播发送到队列中。之后Follwer服务器在接收到消息后首先将事务以事务日志的形式写入到本地磁盘,并且在成功写入后反馈给Leader一个ACK响应,如果不成功则直接丢弃。当Leader服务器在接收到过半数的Follower服务器的ACK响应后,会发出一个Commit广播消息给所有的Follower通知它们提交事务,同时自身也完成事务的提交。
FastLeaderElection选取Leader算法核心步骤:
- 在开始新的一轮选举投票前,logicalclock先自增,用来标识当前选举的时钟周期,同时zk规定只有在同一轮时钟周期内,选票才是有效的;然后将当前机器的ID、之前所处理的最大事务的ID、本机器所处在的主进程周期,等信息封装进选票,开始尝试投票将自己选举为Leader服务器
- 在选票初始化完成之后,本机器开始进行第一次选举投票,zk将已经初始化的选票入sendQueue队列中,由WorkerSender负责发送
- 每台的机器的RecvSender会不断的从recvQueue中获取外部的投票,如果无法从recvQueue中获取到任何选票信息,则当前服务器会立即确认自己是否和其他服务器保持着有效的连接,如果发现没有建立连接,则立即建立连接,如果已经建立连接,则会再次发送自己的投票信息
- 当前机器在向所有机器发送完自己的内部投票后,开始处理收到的外部投票,将收到的选票和自己内部投票进行比较(比较过程同上所写)
- 通过比较选票后,如果判断出外部选票确实优越于内部选票后,则需要进行选票的变更,用外部选票信息覆盖自己的内部选票信息,然后将新的内部选票重新发送出去
- 归档Map会将当前机器收到的本轮周期所有外部选票进行归档
- 在归档完成后,开始进行选票统计;统计的过程就是要确认集群中是否已经有过半的机器认可当前机器的内部投票,如果有过半数同意,则本机器结束投票,否则循环继续从步骤三重新开始
- 如果可以终止投票,则当前服务器开始更新自己的当前状态;如果自己和被过半数的服务器认可的投票所对应的Leader相同,则更新自己的服务状态为Leader,如果不相同,服务器则要根据自己的具体情况来决定将状态变更为FOLLOWING还是OBSERVING