得物 ZooKeeper SLA 也可以 99.99%
内容提要
ZooKeeper(ZK)是一个分布式应用程序协调服务,通过优化版WatchManagerOptimized解决了WatchManager的内存占用问题,提升了性能和稳定性。
延伸解读
WatchManager 内存问题的根源
文章指出,ZooKeeper 默认的 WatchManager 使用 WatchTables 和 Watch2Paths 两个 HashMap 存储 ZNode 与 Watcher 的正反映射关系。当 ZNode 和 Watcher 数量较多,尤其是客户端全量订阅时,关系条数呈指数增长,导致巨大内存开销。例如,20 万 ZNode 和 5000 Watcher 可产生 1 亿条关系,仅存储结构就需约 5.9GB 内存。这是自建集群出现 OOM 的主要原因。
WatchManagerOptimized 的优化机制
优化版通过两项关键改进解决内存问题:一是使用 ConcurrentHashMap 和 ReadWriteLock 替代 synchronized 重型锁,提升并发性能;二是采用位图(BitSet)存储 Watcher 关系,将每个关系压缩为位,大幅降低内存占用。例如,1 万个 Watcher 的位图仅需 1.2KB,而 HashSet 需 305KB,存储效率相差近 300 倍。在极端全订阅场景下,内存占用从 5.9GB 降至 11.7MB,相差 516 倍。
性能提升与稳定性收益
JMH 微基准测试显示,优化版在添加 Watch 操作上比默认版快 6.5 倍,触发 Watch 快 5 倍。灰度测试中,多个集群的内存占用降低 89% 至 91%,选举耗时降低 42% 至 64%,读延迟降低 53% 至 95%,提案和传播延迟从数十万毫秒降至毫秒级。这些改进显著提升了 ZooKeeper 的一致性和稳定性,使 SLA 提升一个数量级。
生产环境配置建议
文章建议将 dataDir 和 dataLogDir 挂载到不同磁盘并使用高性能块存储;对于 ZooKeeper 3.8 版本,使用 JDK 17 并启用 ZGC,3.5/3.6 版本可使用 JDK 8 加 G1;将 SnapshotCount 从默认 100,000 调整为 500,000 以降低磁盘压力;并启用 WatchManagerOptimized。这些配置有助于进一步提升集群稳定性。
Q&A
ZooKeeper的主要功能是什么?
ZooKeeper是一个分布式应用程序协调服务,主要用于管理和协调分布式系统中的数据。
得物技术团队如何提升ZooKeeper的SLA?
得物技术团队通过使用阿里的MSE-ZK,达到了99.99%的SLA可靠性,并进行了稳定性加固。
WatchManagerOptimized是如何解决内存占用问题的?
WatchManagerOptimized通过使用位图存储Watcher关系,显著降低内存消耗,并优化了锁的使用效率。
自建ZooKeeper集群出现内存问题的原因是什么?
自建ZooKeeper集群的内存问题主要是由于WatcherManager管理的childWatches和dataWatches占用大量内存。
优化版WatchManager与默认版的性能差异如何?
优化版在内存占用和操作时间上均优于默认版,性能提升可达6.5倍至516倍。
如何配置ZooKeeper以提升系统稳定性?
建议将dataDir和dataLogDir挂载到不同磁盘,并使用高性能块存储,同时调整SnapshotCount参数。