Disaster Recovery for ZooKeeper - tenji/ks GitHub Wiki

ZooKeeper 容灾

一、ZooKeeper 集群数量为啥要部署成奇数个

ZooKeeper 容错指的是:当宕掉几个 ZooKeeper 节点服务器之后,剩下的个数必须大于宕掉的个数,也就是剩下的节点服务数必须大于 n/2,这样ZooKeeper 集群才可以继续使用,无论奇偶数都可以选举 Leader。例如五台 ZooKeeper 节点机器最多宕掉两台,还可以继续使用,因为剩下三台大于 5/2。

至于为什么最好为奇数个节点?这样是为了以最大容错服务器个数的条件下,能节省资源。比如,最大容错为2的情况下,对应的 ZooKeeper 服务数,奇数为5,而偶数为6,也就是6个 ZooKeeper 服务的情况下最多能宕掉2个服务,所以从节约资源的角度看,没必要部署6(偶数)个 ZooKeeper 服务节点。

ZooKeeper 集群有这样一个特性:集群中只要有过半的机器是正常工作的,那么整个集群对外就是可用的。也就是说如果有2个 ZooKeeper 节点,那么只要有1个 ZooKeeper 节点死了,那么 ZooKeeper 服务就不能用了,因为1没有过半,所以2个 ZooKeeper 的死亡容忍度为0;同理,要是有3个 ZooKeeper,一个死了,还剩下2个正常的,过半了,所以3个 ZooKeeper 的容忍度为1;同理也可以多列举几个:2->0; 3->1; 4->1; 5->2; 6->2 就会发现一个规律,2n和2n-1的容忍度是一样的,都是n-1,所以为了更加高效,何必增加那一个不必要的 ZooKeeper 呢。所以说,根据以上可以得出结论:

从资源节省的角度来考虑,zookeeper集群的节点最好要部署成奇数个!

二、“脑裂”场景说明

三、“脑裂”问题处理

3.1 什么是“脑裂”?

简单点来说,脑裂(Split-Brain) 就是比如当你的 Cluster 里面有两个节点,它们都知道在这个 Cluster 里需要选举出一个 Master。那么当它们两个之间的通信完全没有问题的时候,就会达成共识,选出其中一个作为 Master。但是如果它们之间的通信出了问题,那么两个结点都会觉得现在没有 Master,所以每个都把自己选举成 Master,于是 Cluster 里面就会有两个 Master。

对于 Zookeeper 来说有一个很重要的问题,就是到底是根据一个什么样的情况来判断一个节点死亡 Down 掉了?在分布式系统中这些都是有监控者来判断的,但是监控者也很难判定其他的节点的状态,唯一一个可靠的途径就是心跳, Zookeeper 也是使用心跳来判断客户端是否仍然活着。

这里做下小总结:

  • 假死:由于心跳超时(网络原因导致的)认为 Leader 死了,但其实 Leader 还存活着。
  • 脑裂:由于假死会发起新的 Leader 选举,选举出一个新的 Leader,但旧的 Leader 网络又通了,导致出现了两个 Leader ,有的客户端连接到老的 Leader,而有的客户端则连接到新的 Leader。

3.2 “脑裂”的原因是什么?

主要原因是 Zookeeper 集群和 Zookeeper Client 判断超时并不能做到完全同步,也就是说可能一前一后,如果是集群先于 Client 发现,那就会出现上面的情况。同时,在发现并切换后通知各个客户端也有先后快慢。一般出现这种情况的几率很小,需要 Leader 节点与 Zookeeper 集群网络断开,但是与其他集群角色之间的网络没有问题,还要满足上面那些情况,但是一旦出现就会引起很严重的后果,数据不一致。

3.3 如何解决“脑裂”问题?

要解决Split-Brain脑裂的问题,一般有下面几种种方法:

  • Quorums (法定人数) 方式: 比如3个节点的集群,Quorums = 2, 也就是说集群可以容忍1个节点失效,这时候还能选举出1个lead,集群还可用。比如4个节点的集群,它的 Quorums = 3,Quorums要超过3,相当于集群的容忍度还是1,如果2个节点失效,那么整个集群还是无效的。这是 Zookeeper防止"脑裂"默认采用的方法。
  • Redundant Communications (冗余通信)方式:集群中采用多种通信方式,防止一种通信方式失效导致集群中的节点无法通信。
  • Fencing (共享资源) 方式:比如能看到共享资源就表示在集群中,能够获得共享资源的锁的就是 Leader,看不到共享资源的,就不在集群中。
  • 仲裁机制方式。
  • 启动磁盘锁定方式。

参考链接