1、背景:

我们线上有一套clickhouse集群,5分片2副本总计10个实例,每个实例独占1台物理机,配套混布一个3节点zookeeper集群。

软件版本:centos 7.5  + CK 19.7.3 + ZK 3.4.13

从昨天开始应用写入日志开始堆积,并不断的报错zookeeper session timeout。

登录机器查看clickhouse的errlog,大量的timeout信息:

2021.09.29 05:48:19.940814 [ 32 ] {} <Warning> app.log_dev_local (ReplicatedMergeTreeRestartingThread): ZooKeeper session has expired. Switching to a new session.
2021.09.29 05:48:19.949000 [ 25 ] {} <Warning> app.log_k8s_local (ReplicatedMergeTreeRestartingThread): ZooKeeper session has expired. Switching to a new session.
2021.09.29 05:48:19.952341 [ 30 ] {} <Error> app.log_dev_local (ReplicatedMergeTreeRestartingThread): void DB::ReplicatedMergeTreeRestartingThread::run(): Code: 999, e.displayText() = Coordination::Exception: All con
nection tries failed while connecting to ZooKeeper. Addresses: 10.1.1.1:2181, 10.1.1.1.2:2181, 10.1.1.3:2181

2、诊断

查看zookeeper状态,3个实例都执行

echo stat|nc 127.0.0.1 2181
21ec4ab4d27caf2280d82d76ae4b66ff.png

返回1个leader 2个follower,集群状态是正常的,但是该命令执行很慢。

尝试登录zk实例

sh /usr/lib/zookeeper/bin/zkCli.sh -server 127.0.0.1:2181

进入登录界面后执行 ls /卡顿半天,然后返回timeout。应该是ZK集群通信出了问题,先对其进行滚动重启,重启后问题依然存在。

尝试调优zk参数,当前参数为

tickTime= 2000 
syncLimit = 10
minSessionTimeout = 4000
maxSessionTimeout = 120000
forceSync=yes
leaderServes = yes

调整成

tickTime= 2000 
syncLimit = 100
minSessionTimeout = 40000
maxSessionTimeout = 600000
forceSync= no
leaderServes = no

重启集群后问题依然存在。

我们线上有4套CK集群,每套都独占一套zk,其余3套集群的zookeeper的内存只有几十K,Node count只有几万,而出问题的这套Node count有2千多万,zookeeper进程内存30G左右。

现在怀疑是Node count过多,导致节点通信拥堵,于是想办法降低Node数量:

  • 该环境有一套kafka混用了ZK集群,为其搭建了一套专属ZK集群并将ZK元数据目录删除,node count和物理内存依然很高,问题没有解决。

  • 清理无用表,找出600多个表,执行drop后,node count和物理内存依然很高,问题没有解决。降低Node count的尝试失败。

排查到现在,基本能想到的招数都已用到,再整理一下思路:

  • ZK节点响应很慢,但是集群状态是正常的;

  • CK的insert经常超时,但是偶尔能执行成功;

  • 增大ZK的超时参数,没有丝毫改善

  • ZK的node count非常多,当前的3个ZK实例占用内存很高(RSS一直在30G上下浮动)

zookeeper实例本质是1个java进程,有没有可能是达到内存上限频繁的触发full gc,进而导致ZK服务响应经常性卡顿?

搜索半天没有在机器上发现full gc的日志记录,只能直接验证一下猜想。

在/usr/lib/zookeeper/conf目录下新建1个文件java.env,内容如下:

export JVMFLAGS="-Xms16384m -Xmx32768m $JVMFLAGS"

滚动重启ZK集群,启动完毕后问题依然存在,但是ZK实例的RSS从原来的30G上升到了33G,超出了Xmx上限。

应该是没吃饱,修改一下文件参数

export JVMFLAGS="-Xms16384m -Xmx65536m $JVMFLAGS"

再次重启ZK集群,ZK实例的RSS飙升到55G左右就不再上升,困扰多时的问题也自动消失了,看来刚刚的full gc猜想是正确的。

既然已经证明是JVM heap内存的问题,那么刚刚调整的ZK参数就全部回滚,然后滚动重启ZK集群。

3、溯源

系统自此稳定了,但是zk进程占用的物理内存越来越大,没几天就达到了64G,照这个消耗速度,256G内存被耗光是迟早的事情。

为什么这套zk的node count会这么多,zk进程的RSS这么大?

登录zk,随意翻找一个表的副本目录,发现parts目录居然有8000多个znode,891faccf7d2b8603e722f46e94cb1eb1.png

登录到ck实例,执行

use system
select substring(name,1,6),count(*) from zookeeper where path='/clickhouse/tables/01-01/db/table/replicas/ch1/parts' group by substring(name,1,6) order by substring(name,1,6);
5ff7d7823ca2d48d0bf9f8af8b73ef59.png

该表自从7月后znode part数量就一路飙升,在9月末达到最高值。

尝试执行optimize table table final,对降低part没什么用。

经和开发沟通后获悉,在7月的时候部分表的insert从每10s执行1次改成了1s执行1次,对应的就是part数量的飙升。

将这些表的insert统统改回了每10s执行1次,截止目前(10月28号),10月份的part基本回落到了一个正常值。

至于如何清理已有的znode,目前有2种方法:

  • 将部分离线表导出后drop,然后再导入,操作后znode从2400w下降到了1700w

  • 大部分表的数据都有生命周期,N个月后将不再需要的历史分区直接drop

至少目前可以确信znode不会再暴涨,zk进程的内存也不会继续增加,可以保证clickhouse集群稳定的运行下去。

4、小结

这次案例前后耗费了2天的时间才得以定位原因并解决,又耗费了更长的时间才找到问题根源,距离发稿截止日期已经过了整整1个月,期间没有再复发过。
java进程对Xms和Xmx设置很敏感,线上应用要密切关注其内存占用情况。

作者简介: 任坤,现居珠海,先后担任专职 Oracle 和 MySQL DBA,现在主要负责 MySQL、mongoDB、Redis 和 Clickhouse 维护工作。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐