bigywater.tech
ZooKeeper · 23

大量 ZNode 的性能问题

一个父节点下放几万个子节点会怎样?这篇量 get_children 的延迟随子节点数的变化,再对比分层设计,最后看一个容易被忽略的事实:响应大小的限制其实在客户端。

30000 个 → 9.3 ms
单个父节点下 get_children 的延迟(子节点名 40 字符)
0.24 ms
同样数量分到 100 个父节点后,每个父节点 get_children 的中位延迟
客户端检查响应大小
Java 客户端对 1.3 MiB 的响应断开,kazoo 不检查

扁平大目录

实测 · 本机执行输出
      子节点数      创建用时 s  get_children ms 结果                  
      1000         0.1              0.7 成功                  
     10000         0.9              3.8 成功                  
     20000         1.1              7.6 成功                  
     25000         0.6              9.0 成功                  
     30000         0.5              9.3 成功                  

分层

实测 · 本机执行输出
  创建用时 3.1 s;100 个父节点各 get_children 的中位延迟 0.24 ms、最大 0.30 ms
  服务端 zk_znode_count = 60106

删除

实测 · 本机执行输出
  delete /shard000(有 300 个子节点)→ NotEmptyError
  kazoo delete(recursive=True) 删除 300 个子节点 + 父节点用时 844 ms

Java 客户端的限制

实测 · 本机执行输出
  ls /big → WatchedEvent state:Disconnected type:None path:null zxid: -1 | 126 字符输出
  加 -Djute.maxbuffer=4194304 后 ls /big → 输出 1260000 字符,开头 [child-000000000000000000000000000000000

这一节说明,是否「超过上限」要看是哪个客户端:服务端发出去的响应并没有被限制,是 Java 客户端读到超长的包时主动断开。选客户端库和排查「莫名断开」问题时要想到这一点。

分步演示

大量 znode
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / ZooKeeper 3.9.6(brew 安装的 jar,用 java -cp 直接启动自己的实例,OpenJDK 17 Temurin,JVM 堆 128 MiB)/ 本机回环端口 7300+,数据在临时目录;默认 tickTime=2000;计时用 time.perf_counter,数字每次运行会有小幅抖动。单机,JVM 堆 256 MiB;子节点名 40 个字符、数据为空;Java 客户端用官方 ZooKeeperMain
    • 没有验证:getChildren2 / 分页的 API(没有)、znode 总数达到百万级、大目录上的 watch 通知(会带来整个列表的重读)、快照与重启时间(第 10 篇已测 20 万)
    京ICP备2026067502号-1京公网安备11010502063487号