首发CSDN:徐同学呀,原创不易,转载请注明源链接。我是徐同学,用心输出高质量文章,希望对你有所帮助。
一、前言
ZooKeeper 参考 Paxos 算法,专门设计了一种支持崩溃恢复的原子广播协议 Zab (Zookeeper Atomic Broadcast ),Leader 选举是其核心思想,也是保证ZooKeeper 数据一致性的关键所在。
二、基本概念
1、服务器角色
一个正常运行的zk集群中,一定存在两种服务器角色 Leader 和 Follower ,Leader 负责事务请求处理协调和数据同步以保证数据顺序最终一致性;Follower 具有投票,只读请求可以自己处理,但是事务请求需要转发给Leader 。还有一种角色 Observer ,和 Follower 统称为 Learner ,但是没有投票权,也不参与过半数机制。
2、票据ZXID和myid
在进行Leader 选举时,ZXID 和myid 作为投票依据,比较服务之间ZXID 和myid 的大小来判定票投给谁,最终得票过半数的成为Leader 。
(1)ZXID
ZXID 是事务ID,由Leader 生成,是一个16进制的递增数字,共64位(二进制),由两部分组成:高32位代表当前Leader 任期编号,每进行一次Leader 选举就加一;低32位是事务计数器,递增,整体上是递增的。
ZXID 的作用主要有两个:
- 保证数据的顺序一致性,所有事务请求统一由
Leader 发起提议,严格按ZXID 顺序执行,当前事务请求没有处理完,再来新的事务请求就会阻塞。 ZXID 参与选票依据,ZXID 大的获得选票。
(1)myid
myid 应该比较眼熟吧,在搭建zk集群时,在每个服务的 dataDir 目录下都会创建一个myid 文件,文件中只需要包含一个数字,且不能重复。在zoo.cfg 配置里也可以看到有几行server.A=B:C:D ,A位置就是myid 。
myid 作为zk 服务的ServerId ,简称SID ,并且也是选票依据,当ZXID 相等时,myid 大的获得选票。
三、Leader选举
1、集群第一次启动Leader选举
因为3个服务器的zk集群Leader 选举可能过于简单,所以下面以5个服务器组成的zk集群来看看Leader 选举的过程。
第一次启动,都处于初始状态,ZXID 都是0,myid 分别为1、2、3、4、5。票据(ZXID,myid) 分别为(0,1) 、(0,2) 、(0,3) 、(0,4) 、(0,5) ,如下是具体的投票选举过程:
myid=1 的服务器启动,发起一次投票,服务器1给自己投1票,票数不过半,选举无法完成。myid=2 的服务器启动,再发起一次投票,服务器1和服务器2分别给自己投一票。- 服务器1和服务器2交换选票信息,先比较
ZXID ,都是0,再 比较myid ,服务器2的 myid 比服务器1大,所以服务器1的票据更改为(0,2) ,服务器2不需要更改。 - 统计选票,服务器1和服务器2变更选票信息后再次交换选票,服务器1和服务器2都有两张一样的票
(0,2) ,意为服务器2有两票,但是票数不过半,无法完成投票。 myid=3 的服务器启动,发起投票,此时服务器1和2持有的票据都是(0,2) ,服务器3的票据为(0,3) ,互相交换选票(每个服务器的票据都要广播给其他具有投票权的服务器),服务器3的 myid 比服务器2的 大,服务器1和2变更票据为(0,3) ,服务器3保持不变。- 再次统计选票,服务器3有三张票,服务器1和2都是零票。服务器3持有的票数过半,
Leader 选举完成,服务器3成为 Leader ,服务器1和2成为Follower 。 myid=4 的服务器启动,发现集群中已经有Leader 了,就变更自己的角色状态为Follower 。myid=5 的服务器启动,同理也变更自己的角色状态为 Follower 。
过程还是比较简单,也很容易理解,对投票过程总结3点:
- 交换选票,每个服务器将自己持有的票据广播给其他具有投票权的服务器。
- 变更选票,票据比较,所有服务器都将自己的选票信息更新为那个
ZXID 最大的或者myid 最大的服务器。 - 统计选票,每个服务器再广播一次自己持有的票据,每个服务器判断自己接收到的相同票据数量是否过半数,是就推选该票据的
myid 对应的服务器成为Leader ,其他都为Follower 。
如下图是伪集群服务器数量3的第一次启动过程图:
2、集群运行期间Leader选举
zk集群正常运行期间,一旦选出了Leader,那么所有服务器的角色都不会再发生变化。后启动的服务器发现有Leader ,就直接变更自己的角色为Follower ;Follower 挂了,只要正常运行的服务器数量还在半数以上,整个集群就还可以正常对外提供服务。
但是一旦Leader 所在的服务器挂了,如果集群中正常运行的机器半数以上,就得重选Leader 。
(1)Leader故障的几种典型场景
Leader在挂掉的时候,可能还有一些写操作还没有完成,造成剩下的服务器数据可能不一致。如下是几种Leader故障的场景:
Leader 完成了所有事务COMMIT ,然后挂了,此时过半数以上的服务器的数据是一致的,重选Leader 基本不影响。Leader 在接收到事务请求至最后广播COMMIT 之前某个时刻挂了,这都属于未完成的事务请求,即使有服务器已经把事务请求持久化到了日志文件,但是还没有完成最后的COMMIT 将数据同步到内存数据库。未完成的事务也是会被丢弃。Leader 在广播COMMIT 的过程中挂了,这就导致某些收到了COMMIT 完成了事务,有些没收到,还处于持久化事务日志文件的阶段,最终导致数据不一致。
(2)故障恢复
故障恢复包括两部分:Leader 选举和数据恢复。
Leader 重新选举的过程和第一次启动选举一样,不再赘述。成为新Leader必须满足以下两个条件:
- 新
Leader 的ZXID 是所有服务器中最大的,这就可以最大限度的恢复数据。 - 新
Leader 不能包含未COMMIT 的事务提议,如果事务日志文件中有未同步到内存数据库的事务将会回滚丢弃。
新Leader选举出来以后,就开始恢复数据了。为了集群全局数据一致性,所有的 Follower 上的数据都必须和Leader的一样:
Leader 有的Follower 没有,Leader 将这些事务同步给Follower 。Leader 没有的,Follower 有,Follower 需要回滚丢弃这些所谓超前的事务。
注:如果原先集群运行了一段时间,产生了数据不一致,然后全部停机后,再一个个重启,最好是先去看看每个服务的事务日志文件内记录的最后提交的ZXID ,最大的那个服务先重启,否则可能会导致Leader 已经选举好了,后面一个带着最全数据的服务节点重启了,反而要回滚事务。
需要注意的是在Leader选举的过程中,集群是处于无法正常对外提供服务的状态。
3、Observer旁观者的好处
在Leader 选举的过程中一直没有说Observer ,原因是Observer 没有投票权,但是它除了没有投票权,不参与过半机制外,和Follower 差不多,也可以通过客户端连接,也可以从Leader 同步数据。
Observer的机制可以应用在扩容上,不仅可以实现数据的动态迁移和扩容,还可以就只作为一个与世无争的旁观者,不影响集群的正常写性能,还提升读性能,可以用在异地机房数据同步。
不过这可能导致集群配置混乱,在运维的时候需要分清哪些是Observer 哪些是Follower 。
四、要点总结
熟悉 Leader 选举的底层理论,可以很好的应对集群的迁移和扩缩容。在Leader选举期间和正常服务个数不过半数都无法对外提供服务,并且会丢弃一些未完成的事务,所以 ZooKeeper 是CP ,即保证数据一致性(Consistency )和分区容错性(Partition Tolerance ),但不保证可用性(Available )。
本篇要点总结如下:
Leader 选举,先比较ZXID ,ZXID 较大的获得更多选票,ZXID 相等,比较myid ,myid 较大的获得更多选票。- 票数过半即可选出
Leader ,后来者则自动变为Follower 。 - 故障恢复,Leader重选,会丢弃未完成的事务。
Observer 虽然没有投票权,也不决定过半机制,但是同样可以和 Leader 建立连接,同步数据。这一特点可以用于异地机房同步数据和扩容上,极大提升读性能但对写性能影响极小。
五、参考资料
- 书籍:《从Paxos到Zookeeper分布式一致性原理与实践》
- https://www.bilibili.com/video/BV1to4y1C7gw
- Migrating Kafka’s Zookeeper With No Downtime
如若文章有错误理解,欢迎批评指正,同时非常期待你的评论、点赞和收藏。
如果想了解更多优质文章,和我更密切的学习交流,请关注如下同名公众号【徐同学呀】,期待你的加入。
注:《ZooKeeper-分布式过程协同技术详解》和 《从Paxos到Zookeeper分布式一致性原理与实践》pdf版本由于版权问题无法在CSDN上传,有需要这两本PDF的请关注公众号:徐同学呀,回复zkpdf获取。
|