作者:子葵

背景

在 ZooKeeper 的日常使用过程中,一个令人头疼的问题便是节点的磁盘容量问题,假如因为过大的 TPS 或许不适当的整理战略会导致集群中数据文件,日志文件的堆积,终究导致磁盘爆满,Server 宕机。近期就在线上发现某用户的一个集群在一个时刻段内的 TPS 暴增。

ZooKeeper 避坑实践:SnapCount 设置不合理导致磁盘爆满,服务不可用

导致磁盘中 snapshot 和 transaction log 文件十分多。

ZooKeeper 避坑实践:SnapCount 设置不合理导致磁盘爆满,服务不可用

终究导致磁盘被写满,节点服务不可用。

ZooKeeper 避坑实践:SnapCount 设置不合理导致磁盘爆满,服务不可用

本篇经过深入解读 ZooKeeper 数据文件生成机制,以及 ZooKeeper 中和数据文件生成相关的参数,探求一下 解决 ZooKeeper 磁盘问题的最佳实践。

剖析

ZooKeeper 中生成的数据文件有哪些?

首要咱们需求探求一下 ZooKeeper 对数据进行持久化的基本原理,ZooKeeper 为了确保所有的数据改变不丢掉,选用状态机来进行数据的记载和康复,简略来讲,ZooKeeper 中有一个大 Map 存储所有 Znode,key 便是 Znode 的 Path,value 便是 Znode 中的数据,acl,状态等信息,ZooKeeper 经过在某一时刻点对内存中的大 Map 进行序列化得到这一时刻点内存中数据的一份快照,一起经过另一个文件,存储在此时刻节点之后对这份快照中数据状态的修改操作。

ZooKeeper 避坑实践:SnapCount 设置不合理导致磁盘爆满,服务不可用

当 ZooKeeper 节点重启的时分,会经过现有的 snapshot 和 transaction log 进行数据康复。

ZooKeeper 避坑实践:SnapCount 设置不合理导致磁盘爆满,服务不可用

选用这种做法能够很好的确保内存中已改变的数据不会丢掉,一起写功能不会有太多丢失,在遇到节点宕机之后,能够完整的康复数据。

从此看来,ZooKeeper 发生的数据文件主要有两类:内存中数据的快照文件,以及存储改变的事务日志文件,这两类文件别离经过装备文件中的 dataDir 和 dataLogDir 进行指定,这也是 ZooKeeper 中占用磁盘空间比较大的两类文件。

当 zk 中数据存储过多或许数据改变十分频繁的状况下,将内存中的存储的 snapshot 序列化到文件中,以及数据改变发生的事务日志文件就会很多很大,假如没有装备合适数据整理战略和参数,磁盘问题将会导致集群节点宕机,甚至服务不可用。

public class ZooKeeperServer implements SessionExpirer, ServerStats.Provider {
    ...
    public void takeSnapshot(boolean syncSnap) {
        long start = Time.currentElapsedTime();
        try {
            txnLogFactory.save(zkDb.getDataTree(), zkDb.getSessionWithTimeOuts(), syncSnap);
        } catch (IOException e) {
            LOG.error("Severe unrecoverable error, exiting", e);
            // This is a severe error that we cannot recover from,
            // so we need to exit
            ServiceUtils.requestSystemExit(ExitCode.TXNLOG_ERROR_TAKING_SNAPSHOT.getValue());
        }
            ...
    }
    ...
}

当 Server 测验将内存中的数据写入磁盘的时分,就会发生异常,Server 进程会直接退出。

关于这些问题,ZooKeeper 官方供给一些参数,经过合理的装备参数能够有用的减缓磁盘压力,接下来深入探求一下相关参数。

  • autopu rge.snapRetainCount : (No Java system property) New in 3.4.0
  • autopurge.purgeInterval : (No Java system pro perty) New in3.4.0

首要 ZooKeeper 官方是支撑守时整理数据文件的能力的,经过 autopurge.snapRetainCount 和 autopurge.purgeInterval 指定整理的距离和整理时需求保存的数据文件的个数,这儿需求留意的是,ZooKeeper中敞开此能力需求将 autopurge.purgeInterval 设置为一个大于 0 的值,此值代表整理使命运转的距离(单位是小时)。一般状况下,装备这两个参数敞开守时整理能力之后能够很大程度减轻磁盘的容量压力。可是守时整理使命的最小距离是 1 小时,这在一些特殊场景下无法防止磁盘爆满的问题。

当在两次磁盘的整理距离中,有很多的数据改变恳求时,会发生很多的 transaction log 文件和 snapshot 文件,因为没有到达整理的事件距离,数据累计终究在下一次磁盘整理之前把磁盘写满。为了更好了了解其他参数,咱们先探求一下 ZooKeeper 会在什么时分发生快照文件和事务日志文件。

ZooKeeper 在什么时刻生成这些数据文件

经过在代码中办法引用 (ZooKeeperServer.takeSnapshot),发现在以下状况下会触发新的快照文件的生成

  • 节点发动,加载完数据文件之后
  • 集群中发生新 leader
  • SyncRequestProcessor 中到达一定条件(shouldSnapshot 办法指定)

前两种状况都是非高频的,咱们看一下第三种状况,在 SyncRequestProcessor 的 run 办法中调用了 takeSnapshot 办法,在此之前调用了 shouldSnapshot 进行了判别:

private boolean shouldSnapshot() {
    int logCount = zks.getZKDatabase().getTxnCount();
    long logSize = zks.getZKDatabase().getTxnSize();
    return (logCount > (snapCount / 2 + randRoll))
        || (snapSizeInBytes > 0 && logSize > (snapSizeInBytes / 2 + randSize));
}

其间 logCount 的值是当时事务日志文件的大小以及事务日志的数量,snapCount,snapSizeInBytes 是经过 Java SystemProperty 装备的值,randRoll 是一个随机数(0 < randRoll < snapCount / 2),randSize 也是一个随机数(0 < randSize < snapSizeInBytes / 2),因而这个判别条件的逻辑能够总结为下:

当当时事务日志文件中的事务数量大于运转时发生的和 snapCount 相关的一个随机值(snapCount/2 < value < snapCount)或许当时的事务日志文件的大小大于一个运转是发生的和 snapSizeInBytes 相关的随机值(snapSizeInBytes/2 < value < snapSizeInBytes)就会进行一次 snapshot 的文件写入,而且能够从代码中看到,写入snapshot会将现在的事务日志文件刷入磁盘,并新建一个事务日志文件。

public void run() {
    if (shouldSnapshot()) {
        resetSnapshotStats();
        // 滚动事务日志文件
        zks.getZKDatabase().rollLog();
        // take a snapshot
        if (!snapThreadMutex.tryAcquire()) {
            LOG.warn("Too busy to snap, skipping");
        } else {
            new ZooKeeperThread("Snapshot Thread") {
                public void run() {
                    try {
                        zks.takeSnapshot();
                    } catch (Exception e) {
                        LOG.warn("Unexpected exception", e);
                    } finally {
                        snapThreadMutex.release();
                    }
                }
            }.start();
        }
    }
}

snapCount,snapSizeInBytes 这两个变量能够经过 ZooKeeper 的装备文件中指定。

  • snapCount : (Java system property: zookeeper.snapCount)
  • snapSizeLimitInKb : (Java system property: zookeeper.snapSizeLimitInKb)

由此可知,能够经过在装备文件中装备 snapCount 和 snapSizeLimitInKb 来操控 snapshot 文件发生的频率。

主张

经过以上剖析可知,能够经过 4 个参数对 ZooKeeper 的数据文件生成和整理进行装备。

  • autopurge.snapRetainCount : (No Java system property) New in 3.4.0
  • autopurge.purgeInterval : (No Java system property) New in 3.4.0
  • snapCount : (Java system property: zookeeper.snapCount)
  • snapSizeLimitInKb : (Java system property: zookeeper.snapSizeLimitInKb)

前两个装备项指定 ZooKeeper 的守时整理战略,后两个参数装备ZooKeeper生成快照文件的频率。

依据前面剖析,能够指经过将 snapCount 和 snapSizeLimitInKb 调整大能够减小 snapshot 的生成频率,可是假如设置的过大,会导致在节点重启时,加载数据缓慢,延伸服务的康复时刻,增大事务危险。

autopurge.purgeInterval 最小只能指定为 1,这将整理距离硬约束到 1 小时,针对高频写入情形无法下降危险。

MSE ZooKeeper 针对以上剖析的 ZooKeeper 的磁盘问题,设置了合适的参数,而且用户能够便利更改相关参数实现事务场景适配,而且 MSE ZooKeeper 在 ZooKeeper 本身的数据整理机制外,供给额定保证手法,保证集群免受磁盘问题影响。

ZooKeeper 避坑实践:SnapCount 设置不合理导致磁盘爆满,服务不可用

运营活动

重磅推出 MSE 专业版,更具性价比,可直接从根底版一键平滑升级到专业版!

ZooKeeper 避坑实践:SnapCount 设置不合理导致磁盘爆满,服务不可用

点击此处 ,前往微服务引擎 MSE 官网查看更多~