ZK节点故障的容错方案

ZK节点故障的容错方案

  • 1. 选主逻辑
    • 1.1 什么样的节点应该成为Leader?
    • 1.2 选主的4个阶段
    • 1.2 初始运行的选主过程
    • 1.3 运行过程中异常的选主过程
      • 1.3.1 Leader故障的几种典型场景
      • 1.3.2 故障恢复 - Leader选举
      • 1.3.2 故障恢复 - 数据恢复
  • 4. 疑问和思考
    • 4.1. 如果zk集群全挂了,怎么保持启动顺序?
    • 4.2. 在COMMIT未完成时,Leader宕机,相关的事务数据是否可能会丢失?
    • 4.3. 为什么要设计Observer?
  • 5. 参考文档

本文主要探讨zk集群的高可用容错方案和容错能力的探讨。在出现单机故障时相关的容错方案,如果故障的节点是Leader,重新选择Leader的选主逻辑,以及集群的恢复方案。

更多关于分布式系统的架构思考请参考文档关于常见分布式组件高可用设计原理的理解和思考

1. 选主逻辑

Zk通过ZAB协议通过Quorum机制进行集群的高可用和保证数据一致性,在节点故障场景下,能够通过选主逻辑,选择新的leader,重新提供服务。集群最大能够容忍的故障节点数量为 (n -1) / 2,n表示集群的节点数量(通常要求部署n为单数,如3,5,7)。

选主的目标,是希望选择一个Leader以继续提供服务,分为初始运行选择Leader运行中Leader出现异常重新触发选主

1.1 什么样的节点应该成为Leader?

在讨论选主逻辑之前,应该思考一下,什么样的节点应该成为Leader? 因为投票过程需要比较,比较的依据就是按照成为Leader的标准进行参照!

按照zk的设计,成为Leader的节点应该满足如下条件,在同一个选主周期过程中

  • zxid(事务id)越大,越容易成为Leader(通常写入的数据越多)
  • 如果zxid相同,sid(配置的myid)越大,越容易成为Leader(没有特别原因,长者为尊,避免多次选举)
  • 获取超过集群半数节点的选票的节点,成为Leader

因此整理出,在投票选主过程中,有效的相关因素

属性 作用 说明
zxid 事务ID单调递增 节点zxid越大(数据最新),该节点越容易成为Leader
sid 由myid配置获取 zxid相同,该节点的sid越容易成为Leader
electionEpoch 投票轮数 同一轮投票,统计票数有效
peerEpoch 选举纪元,单调递增 原则上选举纪元大的更有效,如果多个Leader相遇,纪元大的有效(规避脑裂情况)
state 当前服务器的状态

1.2 选主的4个阶段

在同一投票纪元内,

1,阶段1: 各节点给自己投票,投票的信息内容是(sid,zxid),将选票信息广播
2,阶段2: 接受其他节点的广播选票信息,跟当前节点的选对比,根据zxid大、sid大的原则,更改本节点的选票,比如当前节点的选票信息(1,0) < 广播选票(2,0),则当前节点的选票更新为(2,0)
3,阶段3: 完成阶段2的交换选票后,将当前节点交换后的选票广播给其他节点并进行统计,当前节点会根据获取的选票判断自己的角色,并得知对应的Leader节点。如果本节点收到超过集群半数节点选票,当前节点则成为Leader,反之则是Follower
4,阶段4: Follower主动跟Leader进行数据同步,如果Follower能够跟Leader进行通信并进行数据同步,选主结束;否则进行下一轮投票。

1.2 初始运行的选主过程

以5个服务节点组成的zk集群来看看Leader选举的过程。

第一次启动,都处于初始状态,ZXID都是0,myid分别为1、2、3、4、5。票据(ZXID,myid)分别为(0,1)、(0,2)、(0,3)、(0,4)、(0,5)(此概念不涉及源码层面,但是原理上差不多),如下是具体的投票选举过程:

  1. myid=1 的服务器启动,发起一次投票,服务器1给自己投1票,票数不过半,选举无法完成。
  2. myid=2 的服务器启动,再发起一次投票,服务器1和服务器2分别给自己投一票。
  3. 服务器1和服务器2交换选票信息,先比较ZXID,都是0,再 比较myid,服务器2的 myid 比服务器1大,所以服务器1的票据更改为(0,2),服务器2不需要更改。
  4. 统计选票,服务器1和服务器2变更选票信息后再次交换选票,服务器1和服务器2都有两张一样的票(0,2),意为服务器2有两票,但是票数不过半,无法完成投票。
  5. myid=3 的服务器启动,发起投票,此时服务器1和2持有的票据都是(0,2),服务器3的票据为(0,3),互相交换选票(每个服务器的票据都要广播给其他具有投票权的服务器),服务器3的
  6. myid 比服务器2的 大,服务器1和2变更票据为(0,3),服务器3保持不变。
  7. 再次统计选票,服务器3有三张票,服务器1和2都是零票。服务器3持有的票数过半,Leader选举完成,服务器3成为 Leader,服务器1和2成为Follower。
  8. myid=4的服务器启动,发现集群中已经有Leader了,就变更自己的角色状态为Follower。
  9. myid=5 的服务器启动,同理也变更自己的角色状态为 Follower。

1.3 运行过程中异常的选主过程

zk集群正常运行期间,一旦选出了Leader,那么所有服务器的角色都不会再发生变化。后启动的服务器发现有Leader,就直接变更自己的角色为Follower;Follower挂了,只要正常运行的服务器数量还在半数以上,整个集群就还可以正常对外提供服务。

但是一旦Leader所在的服务器挂了,如果集群中正常运行的机器半数以上,就得重选Leader。

1.3.1 Leader故障的几种典型场景

Leader在挂掉的时候,可能还有一些写操作没有完成,可能会造成剩下的服务器数据不一致。如下是几种Leader故障的场景:

  1. Leader完成了所有事务COMMIT,然后挂了,此时过半数以上的服务器的数据是一致的,重选Leader基本不影响(数据不丢失)
  2. Leader在接收到事务请求至最后广播COMMIT之前的某个时刻挂了,这都属于未完成的事务请求,即使有服务器已经把事务请求持久化到了日志文件,但是还没有完成最后的COMMIT将数据同步到内存数据库(事务未完成,会被丢弃)
  3. Leader在广播COMMIT的过程中挂了,这就导致某些收到了COMMIT完成了事务,有些没收到,还处于持久化事务日志文件的阶段,最终导致数据不一致(该数据有可能会被丢失,跟宕机的zk节点启动顺序和启动范围有关系。如5个节点的集群,1,2完成了数据广播和写入,在启动的时候,在集群启动的时候如果只启动了3.4.5,相关的事务就会丢失)

这就会导致数据恢复过程中,需要拆分为Leader选举数据恢复2个阶段

1.3.2 故障恢复 - Leader选举

Leader 重新选举的过程和第一次启动选举一样,不再赘述。

成为新Leader必须满足以下两个条件:

  • 新Leader的ZXID是所有服务器中最大的,这就可以最大限度的恢复数据。
  • 新Leader不能包含未COMMIT的事务提议,如果事务日志文件中有未同步到内存数据库的事务将会回滚丢弃。

需要注意的是在Leader选举的过程中,集群是处于无法正常对外提供服务的状态。如果由于各种原因,导致选主失败,则集群一直无法恢复。

1.3.2 故障恢复 - 数据恢复

为了集群全局数据一致性,所有的 Follower上的数据都必须和Leader的一样,数据同步过程如下

  • Leader有的Follower没有,Leader将这些事务同步给Follower。
  • Leader没有的,Follower有,Follower需要回滚丢弃这些所谓超前的事务。

4. 疑问和思考

4.1. 如果zk集群全挂了,怎么保持启动顺序?

如果原先集群运行了一段时间,产生了数据不一致,然后全部停机后,再一个个重启,最好是先去看看每个服务的事务日志文件内记录的最后提交的ZXID,最大的那个服务先重启,否则可能会导致Leader已经选举好了,后面一个带着最全数据的服务节点重启了,反而要回滚事务。

4.2. 在COMMIT未完成时,Leader宕机,相关的事务数据是否可能会丢失?

从客户端的角度来看,这个事务还没有完成。相关的事务有可能会丢失,也可能不会丢,跟宕机的zk节点启动顺序和启动范围有关系。
如5个节点的集群,1,2完成了数据广播和写入,在启动的时候,在集群启动的时候如果只启动了3.4.5,相关的事务就会丢失

4.3. 为什么要设计Observer?

Observer的职能和功能跟Follower整体上并没有区别,但是Observer没有选票权,不参与投票,也不会参与竞选Leader。这样设计的目的是,能够提升集群的读能力,但是又不必引入过多的投票节点,从而导致造成过多轮投票。过多轮投票选择出Leader,势必会延迟集群的恢复时间。

5. 参考文档

ZooKeeper集群Leader选举理论解读

你可能感兴趣的:(Zookeeper,分布式,中间件,zookeeper,中间件)