news 2026/6/25 22:46:30

腾讯云 NoSQL 技术之 MongoDB 篇:物理备份磁盘膨胀率减少 90% 的内核优化实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
腾讯云 NoSQL 技术之 MongoDB 篇:物理备份磁盘膨胀率减少 90% 的内核优化实践

**作者:**腾讯云cmongo内核团队-杨亚洲、尹超

**关键词:**MongoDB、WiredTiger、物理备份、空间回收、备份回档提速

MongoDB 常见的备份方式有两种:逻辑备份和物理备份。逻辑备份需要逐条遍历集合,期间持续消耗 CPU、内存与业务侧的查询带宽,TB 级实例往往要跑十几个小时,对线上业务影响明显;物理备份则直接以文件粒度拷贝 WiredTiger 的.wt数据文件,IO 几乎是顺序大块读,备份速度通常是逻辑备份的5~10倍以上,对业务读写影响小。也正因如此,物理备份成为大规模 MongoDB 集群的主流选择。

在 MongoDB 的日常运维里,物理备份长期以来都有一个隐蔽的副作用:承接备份任务的 hidden 节点,磁盘占用经常比主节点和普通从节点高出一大截。线上某核心大客户就是典型案例——主节点和普通从节点磁盘占用始终稳定在 1TB 出头,而同副本集内承担备份任务的 hidden 节点,磁盘却随着备份任务的累计运行悄然膨胀到了 1.5TB+,膨胀幅度高达50%,且备份结束后这部分空间并不会自动回收,只能依赖人工介入或重建节点。

为了解决该大客户遇到的膨胀问题,我们对 MongoDB 存储引擎做了深入分析,对 MongoDB 内核物理备份做了深入优化,最终形成了一套从 wiredtiger 内核到旁路服务的完整优化链路。结合线上真实业务场景测试验证,该优化可以保证线上业务99%的 MongoDB 集群,其物理备份膨胀率从之前极端的200%降到5%~10%以内,多表场景整体膨胀率可以减少90%。

这篇文章想把整个过程讲清楚——为什么会膨胀、我们做了什么、为什么这样做是安全的、以及在不同生产场景下实测的效果。

一、背景:MongoDB 物理备份的几个长期痛点

MongoDB 的物理备份并不是把整个 dbpath 直接 cp 走那么简单。它的核心是 wiredTiger 提供的一个叫 backup cursor 的能力:业务连上 mongod,打开一个 backup: cursor,WT 在那一刻冻结一份 checkpoint 视图,给你一份这一刻所有数据文件的清单,旁路备份服务拿着这份清单,通过网络通路,把每个xx.wt文件直接拷贝到 cos 对象存储。整个 cursor 在拷贝期间一直保持打开状态,直到所有文件落到 COS 才会关闭。

听起来挺合理。但只要在线上跑,几个痛点会陆续浮现。

**第一个,也是最明显的——hidden 节点的磁盘占用膨胀。**backup cursor一旦在 hidden 上打开,这个节点的所有.wt文件就开始集体膨胀,业务写入越多,膨胀越严重。更糟糕的是这部分膨胀出来的空间,在 backup 结束之后并不会自动收回。

**第二个,单表膨胀时间随总数据量线性放大。**backup cursor 是“全或无”语义——cursor 不关,全部表都被锁住;cursor 一关,所有表的状态一起恢复。这意味着即便某表已经拷贝完毕,由于还需要拷贝其他表,cursor 一直打开,因此这个拷贝完成的表还是会持续膨胀。

**第三个,oplog 文件被无意义地拷贝。**膨胀率越高的集群,一般其 update 等写入会较高,oplog.wt 占比也会更高,oplog 是 MongoDB 的复制日志,物理备份恢复时根本用不上它——恢复完成后节点会重新从复制集拉最新的 oplog 追齐。但 backup cursor 默认会把 oplog 也列在文件清单里,于是旁路服务老老实实地把它拷到 COS。oplog 是 capped 表,不仅用户请求 update、delete 等会膨胀,甚至 insert 也会膨胀,所以 backup 期间它的膨胀很多时候是所有表里最严重的——典型生产环境,这部分数据拷贝既慢又没有回档价值。

**第四个,膨胀导致备份回档时间越来越长。**膨胀不只是 hidden 节点磁盘账单这一环的事——最终影响旁路服务上传到 COS 的就是这份膨胀后的数据,原本 1TB 的集群极端情况实际要上传 23TB,COS 存储费、跨地域复制流量费同比例上涨;备份期间 hidden 节点到 COS 的上行带宽也成倍占用,多个实例同时备份还会把出口带宽打满,挤压其他业务的网络资源;回档时同样要把这 23TB 重新从 COS 拉回本地、写盘、由 mongod 加载,下载耗时、本地 IO、wiredtiger_open时间全部翻倍,本来半小时能回档完的实例被拖到1-2小时以上。一次膨胀,hidden 磁盘、COS 存储、网络带宽、回档时长四本账单一起被加价。

总结一下,MongoDB 的备份痛点其实是一个连环:膨胀 → 磁盘成本 → 备份慢 → 膨胀更严重 → 恢复也慢,每一环都在给下一环加压。我们的优化要解决的就是这个连环。

二、优化收益

2.1 测试场景

分析腾讯云线上多个膨胀率较高的用户实例,可以确认大部分实例为多个大表实例,通过腾讯云副本集实例模拟多表用户场景测试,写入4个表,每个表 100G 左右,然后启用备份,并且备份期间各个表的读写流量如下:

文件稳态大小业务写入速率/s说明
表1(热表)102 GB约 800 update+800 read频繁update的业务主表,典型中型OLTP场景的热点更新
表2(温表)99 GB约 400 update+800 read中等更新频率,业务次要热表
表3(流水表)101 GB约 1000 insert + 1000 readappend-only insert,例如订单流水/事件日志/IM消息这种典型的写入型业务表
表4(冷表)98 GB100 read只读

2.2 收益对比

腾讯云 MongoDB 优化前后的性能对比总结如下:

指标优化前**(实测)**优化后**(实测)**优化收益
备份总耗时~85.1 min~44.7 min缩短44.4%
hidden节点峰值占用850.9 GB524.3 GB节省326.6 GB
字节数膨胀(hidden相比主节点)+331.8 GB+28.8 GB减少91.3%
膨胀率(严格意义)63.9%5.8%减少91%
COS实际上传量850.9 GB~524.3 GB节省~326.6 GB(−38.4%)
回档要从COS拉回的数据量850.9 GB~524.3 GB少传~326.6 GB(−38.4%)

2.3 优化总结

从上表可以看出,通过优化备份链路,备份耗时、hidden 节点峰值占用、字节数膨胀、COS 实际上传量、备份期间 COS 带宽占用、回档要从 COS 拉回的数据量等指标都有明显改善。该优化最终收益可以总结如下:

  1. 用户集群存储成本:减少38%
  2. 物理备份膨胀率:减少91%
  3. COS 存储成本:减少38.4%
  4. 备份回档效率:提升44.4%
  5. COS 带宽节省:节省38.4%

三、膨胀原理:为什么 backup 一开,hidden 节点就开始涨

要解决膨胀,得先看清楚膨胀的物理过程。这一节会带读者走一遍 WiredTiger 在 backup 期间的内部状态变化,把“为什么会膨胀”讲彻底。

3.1 WiredTiger 的空间回收链路

WT 的存储基本单位是 page,每个 page 在落盘时会占用文件里一段连续的 extent(可以理解成一段连续的字节区间)。一个数据文件(xxx.wt)由很多 extent 拼起来,extent 之间可能有空洞。

WT 维护三个核心的 extent 列表,理解了这三个列表,就理解了整个空间回收逻辑:

列表含义谁能用它
live.alloc本checkpoint周期内分配出去的extent当前checkpoint的活跃page
live.avail当前可以被新写入复用的空闲extent新写入直接分配
live.discard被历史checkpoint引用、暂时不能复用的extent等历史checkpoint被drop后,进入下一轮ckpt_avail

正常情况下,一个 page 被覆盖写(update)或者删除(delete),WT 的处理是这样的:

老 checkpoint 被 drop,是空间回收的唯一触发器。mongo server 内核 checkpoint 默认每60秒触发一次,每次 checkpoint 会把“过时”的老 checkpoint 从元数据里 drop 掉,被 drop 的 checkpoint 所引用的所有 extent 才能进入下一轮的可复用池。

这条链路在没有 backup 的情况下运转得非常顺畅:业务一边写,WT 一边把老 page 进 discard,下一轮 checkpoint 把老 ckpt drop 掉,extent 回到 avail 池,新写入复用空闲位置——文件大小保持稳态。

3.2 backup cursor 是如何打断这条链路的

backup cursor 一打开,WT 立刻做两件事来“冻住”快照:

第一件事,把 backup start 设成最近一次 checkpoint 的时间戳。这个时间戳是后续所有“是否能 drop 老 checkpoint”判断的依据。

第二个判断是膨胀的真正源头,它的作用就是“backup期间,所有 backup 开始之前就存在的 checkpoint 都不能被删,因为旁路服务可能还在通过 backup 视图读这些 checkpoint 引用的数据”。

正常情况下,extent 可被持续复用,如下图所示:

backup 期间,extent 无法复用 → 文件持续膨胀,如下图所示:

膨胀总结:

老 checkpoint 被 pin → 旧 extent 不能进入复用池 → 新写入只能往文件尾部 append。

3.3 oplog.wt 是膨胀最严重的“双冠王”

普通用户数据表的膨胀有上限:

由于普通数据表默认需要保持2个 checkpoint,因此最坏情况下文件大小的理论上限大约为稳态的3倍,一般受 update、delete 相关操作影响。

Oplog 表膨胀上限:

但 oplog 完全不一样,oplog 记录全节点所有写操作——业务写一次, oplog 也写一次。也就是说,oplog 的写入速率可能是任何单张用户表的几十倍。更关键的是,oplog是capped collection,OplogCapMaintainerThread会持续对 oplog 头部做 range truncate 来维持oplogSize配置上限——这个 B-tree 层的 range truncate 在 backup 期间照常进行(给头部 record 打 tombstone、触发后续 reconcile 重写 page),但被淘汰 page 占用的旧 extent 在 backup 期间始终卡在live.discard里、进不到live.avail,新 oplog entry 找不到任何可复用空间,只能持续向文件尾部 append。

理解了“wt 文件 size 只增不减”这一点之后,主从差异就清晰了——可以对比一下同一时刻、同一副本集内,主节点 vs hidden 节点上的 oplog.wt:两者业务负载完全一致(都收到全量写入),OplogCapMaintainerThread也都在正常运行、都在按相同节奏对 oplog 头部做 range truncate(B-tree 逻辑 truncate)。主节点上没有 backup cursor,老 checkpoint 能正常 drop,被淘汰 page 占用的旧 extent 通过live.discard → ckpt_avail → live.avail这条链路回到可复用池,新 oplog insert 直接覆写文件中段那些被腾出来的空洞,文件 size 因此长期稳定在oplogSize配置值附近——稳定的本质是“新写入填回老空洞”,不是 WT 把文件截短了;而 hidden 节点上 backup cursor 持有老 checkpoint,整个老 checkpoint 引用的 extent 都被 pin 住,旧 extent 卡在live.discard里出不来、永远到不了live.avail,新 insert 找不到可复用空间只能向文件尾部 append,于是出现了 oplog 逻辑大小恒定在oplogSize、物理文件 size 线性膨胀的分裂状态。因此,oplog 表除了 update 会膨胀,用户 insert 写入也会膨胀。

更糟糕的是,oplog 拷出去之后在物理恢复时根本用不上——如第一章第三个痛点所述,oplog 是 MongoDB 的复制日志,恢复完成后节点会重新从复制集拉最新的 oplog 追齐,备份产物里的那份 oplog.wt 在回档时会被直接丢弃。

所以,oplog.wt 在很多时候是膨胀最严重 + 拷贝价值最低的“双冠王”。这是后面优化里会被特殊处理的对象。

四、优化过程:内核深度优化

膨胀的物理原理梳理清楚之后,优化方向就比较自然了:让外部备份旁路服务告诉 WT “这张表我已经拷完了,你可以恢复它的空间回收”。我们把这个能力做成了 WT 的一个新 C API,叫WT_CONNECTION::backup_release_checkpoint。围绕这个 API,内核侧做了配套改造。整个优化分成三大块。

4.1 内核侧改造一:精细化释放 checkpoint

新增的 API 语义很简单:旁路服务每拷完一张表,就调一次releaseBackupCheckpoint(ident)通知 WT。WT 把这个 ident 加到一个内部 hash 表里。下一次内部 checkpoint 触发时,做“是否能 drop checkpoint”判断时多查一次这个 hash 表——如果命中,就跳过backup start的 pin 逻辑,让这张表的老 checkpoint 进入正常的 drop 流程,extent 进入ckpt_avail,新写入复用——膨胀停止。

光有 API 还不够,旁路侧的拷贝顺序同样关键。一张表从__backup_start到被 release 之间的这段时间,就是它的“膨胀窗口”——窗口越长,文件被 append 出去的字节越多。所以最优策略是:按预估膨胀率从高到低排序,优先拷贝膨胀最快的表,让它们的窗口尽量短;膨胀慢的冷表/只读表放到最后,反正它们就算晚 release 也涨不了多少。

在我们的实测里(4表场景,1个 update 热表 + 1个 update 温表 + 1个 insert 流水表 + 1个只读冷表),把这个调度策略加上之后,hidden 节点磁盘峰值又能再降20%~30%。

整个流程可以用伪代码描述如下:

端到端流程图(以本测试用例为例)

下图把上述“4 张用户表 +oplog”的实测场景串成一张时序图,体现出“open backup cursor→立刻release oplog→按膨胀率降序拷表→每拷完一张就release一张→最后close cursor”的完整链路:

图中两个关键差异(相对优化前):

第0步:

就把 oplog 这张膨胀最快的 capped 表从 backup snapshot 中摘掉,避免它在整个备份窗口里一直累积 freed block。

第1步:

每完成一张高膨胀率用户表的上传,就立刻对该表调用一次 WT_SESSION::backup_release_checkpoint(uri),让该表的 freed block 在备份还没结束时就开始回流 free list —— 这是 hidden 节点峰值占用从 850.9GB 降到 524.3GB 的直接来源。

4.2 内核侧改造二:oplog 不备份,并第一时间释放 ckpt

接下来是 oplog.wt 这个“双冠王”的处理。前面已经论证过,oplog.wt 在backup 期间膨胀最严重、拷贝价值最低。我们的处理是两步走。

**第一步,在 backup 一开始就立即 release oplog:**备份方拿到 file_list 之后,第一件事不是开始拷贝,而是先识别出local.oplog.rs对应 ident(通常是collection-X-<oplogId>.wt),立即调用releaseBackupCheckpoint(oplog_ident)。WT 收到通知后,下一次内部 checkpoint(~60秒内)就会把 oplog 的老 ckpt drop 掉,oplog.wt 在整个 backup 期间保持稳态大小。

**第二步,备份流程跳过 oplog.wt 的拷贝:**既然 release 之后 WT 可能在任何时候开始改写 oplog 的 extent,我们就不能再去拷贝它了。但简单跳过会带来一个新问题:MongoDB 的 catalog 元数据(_mdb_catalog.wt)和 WT 的 metadata(WiredTiger.wt)里都记录了local.oplog.rs这张集合,恢复时如果元数据指向一个不存在的 wt 文件,mongod 会启动失败。具体解决优化方法参考我们之前的分享: 《「腾讯云NoSQL」技术之MongoDB 篇:MongoDB 存储引擎备份性能70%提升内幕揭秘》

备份端流程图如下(跳过 oplog.wt):

恢复端流程图如下:

4.3 内核侧改造三:crash recovery 路径切换

前面还有一个潜在问题没处理——backup 期间 mongod 进程异常退出之后的恢复路径。

内核逻辑是:mongod 启动时检查 dbpath,看到WiredTiger.backup元文件,就认为这是个 backup 中间状态,走 hot backup restore 路径,从 backup 开始那一刻的 LSN 重放 WAL。如果 backup 已经持续了几小时,这一段 WAL 可能有 几百GB,重放下来要数十分钟级。

目前内核没有给 mongod 留下“上次进程死亡时是否处于 backup 中”这个信息——WiredTiger.backup元文件本身只能表示“曾经开过 backup”,无法区分“backup 还活着”和“backup 已经完成但元文件未清理”两种情况,更没法和“crash 自愈”逻辑联动。我们的做法是在 mongod 进程内部引入一个 sentinel 标记文件(backup.in_progress,下文简称sentinel),让进程自己负责打标和清理;下次启动时由 mongod 启动路径自检该文件是否残留,自动完成WiredTiger.backup的invalidate。整个机制全部内聚在mongod 内部,运维侧零介入,不需要外部 wrapper、systemd 钩子或人工脚本。

sentinel 的三处时机如下:

五、注意事项:备份过程中crash异常处理与解法

还有一类隐患是 crash 后 mongod 自身能否正常启动:backup 期间若有部分表已 release,其老 ckpt 已被 drop、extent 已被新写入覆盖;若此时crash,dbpath 里WiredTiger.backup元文件仍然残留,原生路径会据此走 hot backup restore,试图按“backup开始那一刻的视图”打开这些表,而它们的老 ckpt 在物理上已不复存在——轻则wiredtiger_open报元数据不一致,重则读到错乱数据,启动直接失败。也就是说,release 优化本身在语义上已经让原生 hot backup restore 路径不再可用,必须有配套的启动路径切换。

上文介绍的4.3节的 sentinel 方案也把这条路径堵住了:mongod 启动时若发现backup.in_progress残留,会主动把WiredTiger.backup删除掉,强制走 normal crash recovery——只依赖最新checkpoint + WAL,不再尝试还原任何已被 release 的视图,正确性与启动速度一次性兜住。

换句话说,4.3节的优化不只是“启动快了10x”,更是 release 机制能安全上线的必要前提。

六、总结

这次备份膨胀的优化,本质上是把 WiredTiger 的 hot backup 从“全或无”语义升级到“表级粒度”,把备份对生产环境的副作用降到最低。

结合本文这套表级 release 优化 + 凌晨低峰期错峰备份的调度策略,腾讯云MongoDB 线上99%的实例膨胀率可以从最极端场景下的200%收敛到5%~10%的稳定区间——这意味着 hidden 节点的磁盘 buffer 预留可以从原来的2x大幅压缩到1.1x以内,单实例磁盘成本下降约60%,TB 级大集群的备份窗口也不再需要为膨胀峰值留冗余。剩余不足1%的极端场景(典型如备份期间超大写入压力 + 单表体量极大)我们通过无损在线 compaction 进一步优化解决——在不阻塞业务读写、不影响主从同步的前提下回收备份遗留的碎片空间,把这部分尾部实例也拉回到正常水位。

该方案的技术细节与生产实践我们将在后续文章中专门分享。

**客户价值:**前文提到的线上客户多表核心集群在启用本优化后,集群在数月间进行了上百次全量物理备份,Hidden 节点磁盘占用空间始终保持平稳,未再因备份导致的显著空间膨胀、需人工介入清理的情况。整体 Hidden 节点的磁盘空间膨胀率由约50%下降至5%,单节点磁盘空间节省约500GB。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/25 22:45:45

Physics-Informed Neural Networks(PINNs)原理与反演PDE实战

1. 项目概述&#xff1a;当物理定律成为神经网络的“硬约束”你有没有遇到过这样的困境&#xff1a;手头有一组实验测得的温度场数据&#xff0c;分布在不规则的金属散热片表面&#xff0c;想反推材料内部的热导率分布——但传统数值反演方法要么需要精确的初始猜测&#xff0c…

作者头像 李华
网站建设 2026/6/25 22:43:34

深度神经网络实战入门:从原理理解到工业级调优

1. 这不是“又一篇”深度学习科普——而是一份我带三届实习生时反复打磨的实战入门手记你点开这篇内容&#xff0c;大概率正站在两个路口之间&#xff1a;一边是网上铺天盖地的“5分钟看懂CNN”“PyTorch速成班”&#xff0c;讲得天花乱坠却连模型跑不通时的报错都解释不清&…

作者头像 李华
网站建设 2026/6/25 22:36:49

CPU本地运行大模型:量化压缩与GGUF推理实战指南

1. 项目概述&#xff1a;当大模型不再依赖显卡&#xff0c;CPU也能跑出生产力 “我在CPU上跑了AI模型&#xff0c;这确实是未来。”——这句话刚看到时&#xff0c;我下意识皱了眉头。不是怀疑&#xff0c;而是太熟悉那种“标题党式兴奋”背后的落差&#xff1a;要么是跑了个1…

作者头像 李华
网站建设 2026/6/25 22:28:51

WeChatPad:微信双设备登录终极指南,轻松实现手机平板同时在线

WeChatPad&#xff1a;微信双设备登录终极指南&#xff0c;轻松实现手机平板同时在线 【免费下载链接】WeChatPad 强制使用微信平板模式 项目地址: https://gitcode.com/gh_mirrors/we/WeChatPad 还在为微信只能在一台设备上登录而烦恼吗&#xff1f;WeChatPad为你提供了…

作者头像 李华
网站建设 2026/6/25 22:28:40

数据科学为什么没有标准答案:从业务模糊性到技术路径选择

数据科学这行干久了&#xff0c;我越来越觉得一个事儿特别有意思&#xff1a;同样一道题&#xff0c;十个人来解&#xff0c;能给出八种完全不同的方案&#xff0c;剩下两个可能还互相看不惯——不是谁对谁错&#xff0c;而是路径、工具、取舍逻辑全都不一样。这根本不是“答案…

作者头像 李华