专栏名称: 数据分析与开发
伯乐在线旗下账号,分享数据库相关技术文章、教程和工具,另外还包括数据库相关的工作。偶尔也谈谈程序员人生 :)
目录
相关文章推荐
数据中心运维管理  ·  浅谈AHU风墙群控在数据中心的应用 ·  昨天  
数据分析与开发  ·  别再做“职场透明人”!现在开始掌控人生剧本! ·  2 天前  
数据中心运维管理  ·  突发!新加坡一数据中心起火,一人就医 ·  3 天前  
数据分析与开发  ·  请数据人立即拿下软考证书(政策风口) ·  4 天前  
51好读  ›  专栏  ›  数据分析与开发

ZooKeeper 的一致性算法赏析

数据分析与开发  · 公众号  · 数据库  · 2016-11-09 20:16

正文

(点击 上方蓝字 ,快速关注我们)


来源:乒乓狂魔

链接:my.oschina.net/pingpangkuangmo/blog/778927


1 ZAB介绍


ZAB协议全称就是ZooKeeper Atomic Broadcast protocol,是ZooKeeper用来实现一致性的算法,分成如下4个阶段。


先来解释下部分名词


electionEpoch:每执行一次leader选举,electionEpoch就会自增,用来标记leader选举的轮次


peerEpoch:每次leader选举完成之后,都会选举出一个新的peerEpoch,用来标记事务请求所属的轮次


zxid:事务请求的唯一标记,由leader服务器负责进行分配。由2部分构成,高32位是上述的peerEpoch,低32位是请求的计数,从0开始。所以由zxid我们就可以知道该请求是哪个轮次的,并且是该轮次的第几个请求。


lastProcessedZxid:最后一次commit的事务请求的zxid


  • Leader electionleader选举过程,electionEpoch自增,在选举的时候lastProcessedZxid越大,越有可能成为leader


  • Discovery:第一:leader收集follower的lastProcessedZxid,这个主要用来通过和leader的lastProcessedZxid对比来确认follower需要同步的数据范围第二:选举出一个新的peerEpoch,主要用于防止旧的leader来进行提交操作(旧leader向follower发送命令的时候,follower发现zxid所在的peerEpoch比现在的小,则直接拒绝,防止出现不一致性)


  • Synchronization:follower中的事务日志和leader保持一致的过程,就是依据follower和leader之间的lastProcessedZxid进行,follower多的话则删除掉多余部分,follower少的话则补充,一旦对应不上则follower删除掉对不上的zxid及其之后的部分然后再从leader同步该部分之后的数据


  • Broadcast正常处理客户端请求的过程。leader针对客户端的事务请求,然后提出一个议案,发给所有的follower,一旦过半的follower回复OK的话,leader就可以将该议案进行提交了,向所有follower发送提交该议案的请求,leader同时返回OK响应给客户端


上面简单的描述了上述4个过程,这4个过程的详细描述在zab的paper中可以找到,但是我看了之后基本和zab的源码实现上相差有点大,这里就不再提zab paper对上述4个过程的描述了,下面会详细的说明ZooKeeper源码中是具体怎么来实现的


2 ZAB协议源码实现


先看下ZooKeeper整体的实现情况,如下图所示



上述实现中Recovery Phase包含了ZAB协议中的Discovery和Synchronization。


2.1 重要的数据介绍


加上前面已经介绍的几个名词


  • long lastProcessedZxid:最后一次commit的事务请求的zxid


  • LinkedList committedLog、long maxCommittedLog、long minCommittedLog:ZooKeeper会保存最近一段时间内执行的事务请求议案,个数限制默认为500个议案。上述committedLog就是用来保存议案的列表,上述maxCommittedLog表示最大议案的zxid,minCommittedLog表示committedLog中最小议案的zxid。


  • ConcurrentMap outstandingProposalsLeader拥有的属性,每当提出一个议案,都会将该议案存放至outstandingProposals,一旦议案被过半认同了,就要提交该议案,则从outstandingProposals中删除该议案


  • ConcurrentLinkedQueue toBeAppliedLeader拥有的属性,每当准备提交一个议案,就会将该议案存放至该列表中,一旦议案应用到ZooKeeper的内存树中了,然后就可以将该议案从toBeApplied中删除


对于上述几个参数,整个Broadcast的处理过程可以描述为:


  • leader针对客户端的事务请求(leader为该请求分配了zxid),创建出一个议案,并将zxid和该议案存放至leader的outstandingProposals中


  • leader开始向所有的follower发送该议案,如果过半的follower回复OK的话,则leader认为可以提交该议案,则将该议案从outstandingProposals中删除,然后存放到toBeApplied中


  • leader对该议案进行提交,会向所有的follower发送提交该议案的命令,leader自己也开始执行提交过程,会将该请求的内容应用到ZooKeeper的内存树中,然后更新lastProcessedZxid为该请求的zxid,同时将该请求的议案存放到上述committedLog,同时更新maxCommittedLog和minCommittedLog


  • leader就开始向客户端进行回复,然后就会将该议案从toBeApplied中删除


2.2 Fast Leader Election


leader选举过程要关注的要点:


  • 所有机器刚启动时进行leader选举过程


  • 如果leader选举完成,刚启动起来的server怎么识别到leader选举已完成


投票过程有3个重要的数据:


  • ServerState目前ZooKeeper机器所处的状态有4种,分别是


    • LOOKING:进入leader选举状态

    • FOLLOWING:leader选举结束,进入follower状态

    • LEADING:leader选举结束,进入leader状态

    • OBSERVING:处于观察者状态


  • HashMap recvset用于收集LOOKING、FOLLOWING、LEADING状态下的server的投票


  • HashMap outofelection用于收集FOLLOWING、LEADING状态下的server的投票(能够收集到这种状态下的投票,说明leader选举已经完成)


下面就来详细说明这个过程:


  • 1 serverA首先将electionEpoch自增,然后为自己投票serverA会首先从快照日志和事务日志中加载数据,就可以得到本机器的内存树数据,以及lastProcessedZxid(这一部分后面再详细说明)初始投票Vote的内容:


    然后该serverA向其他所有server发送通知,通知内容就是上述投票信息和electionEpoch信息


    • proposedLeader:ZooKeeper Server中的myid值,初始为本机器的id


    • proposedZxid:最大事务zxid,初始为本机器的lastProcessedZxid


    • proposedEpoch:peerEpoch值,由上述的lastProcessedZxid的高32得到


  • 2 serverB接收到上述通知,然后进行投票PK如果serverB收到的通知中的electionEpoch比自己的大,则serverB更新自己的electionEpoch为serverA的electionEpoch如果该serverB收到的通知中的electionEpoch比自己的小,则serverB向serverA发送一个通知,将serverB自己的投票以及electionEpoch发送给serverA,serverA收到后就会更新自己的electionEpoch


    在electionEpoch达成一致后,就开始进行投票之间的pk,规则如下:


/*

* We return true if one of the following three cases hold:

* 1- New epoch is higher

* 2- New epoch is the same as current epoch, but new zxid is higher

* 3- New epoch is the same as current epoch, new zxid is the same

*  as current zxid, but server id is higher.

*/

return (( newEpoch > curEpoch ) ||

(( newEpoch == curEpoch ) &&







请到「今天看啥」查看全文