MongoDB 副本集内存装不下了?Percona 实测分片之外的第三条路

架构:应用连代理,按数据年龄决定落哪一层

问题:副本集装不进内存了

MongoDB 副本集的标准剧本:数据涨、WiredTiger 缓存装不下、每次未命中都是磁盘读,延迟跟着磁盘走而不是跟着查询走。作者遇到的真实场景是 p95 70ms——不是索引烂,是磁盘已经顶到 IOPS 上限。

常规答案有两个:加机器、上分片。分片的问题在于动静太大:分片键定了难改、要 mongos、要 config server、要 balancing,应用写法还得围着分片键来。而作者想回答的问题是:能不能不动应用,把最近的数据放到前面快的地方,老集群原样留着?

Thermocline 的做法:按年龄分层

作者的方案是一个叫 Thermocline 的存储分层工具(Apache 2.0,个人 PoC 项目,发在 Percona 官方博客)。现有集群原封不动变冷层(archive),前面加一个小副本集当热层,应用改连代理。四个组件保证两层一致:

组件 职责
Proxy 说 MongoDB 线协议;热层能证明”答案全在我这”就走热层,否则走冷层;写全走热层
Feeder 读热层 change stream,把每一笔写镜像进冷层
Evictor 文档超过设定年龄、且确认冷层已有,就从热层删
Warmer 把冷层里比年龄线新的数据搬进热层

热层从空开始,靠流量和 warmer 自己填满,全程不用人工搬数据。

测试环境:故意选了不讨好的配置

测试跑在 Hetzner Cloud 上,条件相当苛刻:

项目 配置
冷层(原集群) 3 成员副本集,4000 万条模拟订单,47GB 数据
冷层限制 每成员限 2.5GB 内存(WT 缓存 1GB),数据盘限 3000 读/秒
数据/缓存比 约 47 倍
热层 第二个 3 成员副本集,初始为空
负载 YCSB 0.17,64 线程,每次 10 分钟跑两遍

三种负载混合:在线商城(85% 读)、纯读(95% 读)、集中读(95% 读且 99% 活动落在最新 9% 的文档上)。对照组和实验组跑同一 MongoDB 版本、同样限额、同一客户端。

结果:同一块盘,吞吐 1.6 到 5.2 倍

同硬件吞吐对比:1.6x 到 5.2x,全部零拒绝

核心数字:同一个被限死的集群,挂上 Thermocline 后吞吐提升 1.6x 到 5.2x,所有测试 0 次操作被拒。越靠近”读最近数据”的真实业务,收益越大——集中读场景从 4,447 ops/s 冲到 23,340 ops/s,读延迟 p95 从 86ms 降到 6ms,p99 从 92ms 降到 8ms。

同负载下的延迟对比:读不再等磁盘

更说明问题的是同负载对照:按老集群自己最大能力压 6,165 ops/s,裸跑只能交付 6,086(读 p95 70.5ms);挂上分层后交付 6,156/6,159,读 p95 只有 2.8ms / 2.1ms。冷层磁盘读从 2,704/秒 降到 1,838/秒。

冷启动采用曲线:空热层 100 秒后反超

冷启动测试(热层从空开始):30 分钟平均 8,221 ops/s,比冷层裸跑(5,803)高 42%;100 秒就追平裸跑数字,前 5 分钟平均 6,417、后 5 分钟 9,146,过程中从冷层上搬了 271,271 条文档。

作者自己划的边界

这部分是文章可信度的来源,作者把不赚钱的地方写得比赚钱的还细:

  • 写还是要落冷层。每笔写都镜像给老集群,镜像更新意味着冷层要先从盘上把文档读出来——商城负载里更新延迟和裸跑一样,p99 甚至更差(455-472ms vs 104-189ms),作者说这个尾巴还在修;
  • 读历史数据没有改善,查询只要超出热层年龄线就回冷层,读均匀分布在整个历史上的负载基本没收益;
  • 两层必须同版本,冷层要能听懂应用自己的查询,老集群太旧得先升级;
  • 这是 PoC,测试套件齐全,但没跑过生产,别急着上真实流量。

参考链接

  • 原文:https://www.percona.com/blog/an-alternative-to-sharding-a-mongodb-replica-set-that-outgrew-its-memory-a-proof-(2026-11-02,Zelmar Michelini,Percona 官方博客)
  • 项目地址:https://github.com/zelmario/thermocline(Apache 2.0)

小小点评

  1. 最漂亮的数字(5.2 倍)来自”99% 活动落在最新 9% 数据”的负载——这恰好是国内电商、订单、日志类系统的真实形状:绝大多数查询打最近几天,历史数据只是躺在盘上。分层思路吃的就是这个时间局部性。

  2. 作者自己承认更新延迟的尾巴变差了(p99 455-472ms),这才是文章可信的地方。写多的系统先按自己的负载混合比测一遍再下结论,别直接信他的 5.2 倍。

  3. 对国内 IT 运维和开发者:国内团队遇到”副本集装不进内存”,常规动作就是加副本上分片,Thermocline 的价值是给出”不改应用代码”的第三条路——Apache 2.0、无厂商锁定、还是 Percona 官方博客背书的项目。就算这个工具本身还到不了生产,”按年龄分层 + 代理路由”这套方法可以直接搬到 MySQL/PG 冷热分离场景,值得存进技术选型笔记。

您可能还喜欢...

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注