首页 > 首页 > 应用服务 > 分布式协调 > ZooKeeper > ZooKeeper避坑01:Zxid溢出zxid lower 32 bits have rolled over导致重新选主
2024
05-15

ZooKeeper避坑01:Zxid溢出zxid lower 32 bits have rolled over导致重新选主

一、问题背景

线上业务使用 zooKeeper 做元数据中心以及集群选主,某天业务突然报错连接zookeeper异常,查看zooKeeper server 日志出现报错:zxid lower 32 bits have rolled over, forcing re-election, and therefore new epoch start

ZooKeeper避坑01:Zxid溢出zxid lower 32 bits have rolled over导致重新选主 - 第1张  | 架构迷

二、问题原因

什么是zxid,它是怎么产生的?

首先我们了解一下什么是 zxid,它是怎么产生的:zxid 是 ZooKeeper 中一个事务的全局唯一 id,通过 zxid 描述各个事务之间的全序关系。客户端对 ZooKeeper 内部数据的变更都是通过事务在 ZooKeeper 集群内的传播和处理完成的,因此 zxid 就是客户端对数据进行一次变更所产生的事务在全局事务中的一个唯一 id,这个 id 描述了本次变更的事务在全局事务中的位置,并且不会有两个不同的事务拥有相同的 zxid(全序关系)。

ZooKeeper避坑01:Zxid溢出zxid lower 32 bits have rolled over导致重新选主 - 第2张  | 架构迷

zxid 是一个 64bits 的数,有两个部分组成:当前选举周期(epoch,占用高32bits)以及计数部分(counter,占用低32bits),epoch 表示 leader 关系的变化,每产生一个新的事务,zxid的低32位就会自动加1。当zxid达到最大值,即zxid的低32位达到0xffffffff,就会触发集群强制选主,并重置zxid低32位的计数值,此时zxid高32位变为新Leader的周期,低32位变为0。

ZooKeeper  集群选主成功之后保证只会有一个Leader,并且此 Leader 的 epoch 是以前没有使用过的,这就保证了只会有一个 leader 使用本次选举过程中产生的 epoch, 在此基础上,每当客户端对数据进行变更的时候,leader 对产生的事务在当前 counter 的值加一产生新的事务的 zxid,并使用此 zxid 将此事务在集群中进行同步,这样就保证了事务的全序关系。

ZooKeeper避坑01:Zxid溢出zxid lower 32 bits have rolled over导致重新选主 - 第3张  | 架构迷

为什么 zxid 溢出需要重新选主

通过研究 zxid 的组成,可以发现,当单个 epoch 中处理的事务过多,以至于当前epoch 对应的 counter 数值超过了 32bits 计数的最大值,如果继续计数 epoch 就会 +1 , 如果在未来,进行了一次选举,其他的 Server 当选了 leader,但是他产生的新 epoch 可能就会和现在 zxid 中的 epoch 重合,导致不同的事务会有相同的 zxid,破坏了事务之间的全序关系,可能导致脏数据的产生。因此 ZooKeeper 在低 32 位达到最大计数值的时候,就会主动产生一次选主,避免以上问题。

为什么日志出现了shutdown,zk的进程依旧是存在的

遇到zxid溢出等错误,导致shutdown并引发重新选举,相当于把leader给先关闭了,然后重新获取一个新的角色提供服务(LeaderZooKeeperServer/FollowerZooKeeperServer等)。这个新服务对像的指标数据存储对象(父类对象ZooKeeperServer#serverStats)就是初始化状态,总体来说:这里就等于在不关闭java进程的情况下做了“重启”。

ZooKeeper 集群选主会产生什么影响

一般情况下使用 ZooKeeper 作为注册配置中心,集群选主对于客户端来说是无感知的,集群选主之后客户端会主动重连恢复,但是对于依赖于 ZooKeeper Disconnected 事件的应用,可能会受到影响,在集群选主的时候,Server会向客户端返回 Disconnected 事件,例如 Curator recipes 中 LeaderLatch 类型,在 ZooKeeper 集群选主的时候,LeaderLatch 会重新分配 Leader。

三、解决办法

针对Zxid溢出的BUG问题,目前官方还没有给出修复方案。有网友提了个PR但是官方没有merge,需要自己打patch,风险较高,暂不考虑,到目前 ZooKeeper 3.9 版本此BUG发都没有被解决,还是open状态:https://issues.apache.org/jira/browse/ZOOKEEPER-2789

目前,Server没有规避zxid溢出的方法,需要在业务侧提前规避。

    • 对于使用ZooKeeper作为注册配置中心的使用场景
      1. 集群选主不会影响到正常使用,客户端在集群选主之后会自动重连恢复。
      2. 可以针对Zxid数量添加监控,到达报警阀值后,手动在业务低峰期进行zk leader节点的重选操作,用以重置Zxid数量
    • 对于依赖于客户端Disconnected事件的使用场景:
      1. 例如,Curator Recipes中的Leader Latch场景,在集群选主时,Server会产生Disconnected事件,表示Client与Server断开了连接。在选主完成之后,Client会通过重连机制重新连接到Server。
      2. 在产生Disconnected事件后,Leader Latch会重新选主(这里的选主是指使用LeaderLatch做集群选主的场景中,重新选择注册上来的其他实例作为主),此时可能会触发业务侧的一些其他逻辑。例如,在Flink 1.14及以下版本使用LeaderLatch作为选主的默认实现,在ZooKeeper集群选主时会导致Flink Job被重启。
      3. 在业务的实现中,可通过Curator Recipes中的Leader Selector替换Leader Latch的方法,对ZooKeeper短暂的断连做一定的容忍。
    ZooKeeper避坑01:Zxid溢出zxid lower 32 bits have rolled over导致重新选主 - 第4张  | 架构迷

    参考文章:

    https://developer.aliyun.com/article/1155595?utm_content=m_1000368093

    https://help.aliyun.com/zh/mse/support/handle-the-issue-that-a-zxid-overflow-occurs-when-a-zookeeper-instance-is-used

    最后编辑:
    作者:摘星怪
    这个作者貌似有点懒,什么都没有留下。

    留下一个回复

    你的email不会被公开。