「腾讯云NoSQL」技术之 MongoDB 篇:MongoDB 存储引擎备份性能70%提升内幕揭秘

在非结构化数据爆炸式增长的今天,MongoDB 凭借其灵活的文档模型与分布式集群架构成为游戏行业高并发场景的首选数据库。然而,在大文档场景,oplog 的备份和回档长期存在效率瓶颈------传统备份方式需全量拷贝数据目录,导致资源浪费。基于以上业务痛点,腾讯云 NoSQL 与内核团队深入解析 WiredTiger 存储引擎原理,新增 exclude_target 功能,通过智能识别并跳过冗余的无用 oplog.wt 文件,实现备份耗时、存储成本及网络带宽的70%缩减,为游戏、电商等高并发场景提供更高效的运维支持。

作者:腾讯云NoSQL团队-杨亚洲、尹超

线上有部分 MongoDB 业务,因其业务独特性产生了海量的非结构化数据(如游戏日志、用户行为记录等),常面临文档大、更新写入频繁的挑战。为了存储足够的oplog ,运维人员往往需要将 oplog size 设置得比较大,甚至远远超过了业务数据本身的大小。MongoDB 物理备份基于 wiredTiger 存储 backup 功能实现,该功能主要拷贝数据目录中的所有 wt 文件,包括用户数据文件、oplog 、系统 wt 文件等。

数据目录中所有 wt 文件越大,客户的物理备份及回档时间也就越长,下面以某核心游戏业务为例进行说明,该业务对应实例不同 wt 文件数据量总结如下:

真实业务数据大小:230G

Admin 、config 等系统数据大小:< 1G

Oplog 大小(oplog.rs 表): 515G

从上面的统计可以看出,oplog 数据大小占比约70%。由于社区 MongoDB 存储引擎的 backup 物理备份功能必须对数据目录所有文件做备份,回档才会成功,这意味着每次备份都有大量资源浪费在重复备份 oplog 上,导致备份和回档时间过长,存储成本和网络带宽消耗巨大。

基于以上的业务痛点,腾讯云对 MongoDB 存储引擎的 backup 做了一次深入分析及优化,基于 wiredTiger 存储引擎 backup 新增 exclude_target 功能,该功能可以忽略备份指定 wt 文件,同时可以做到只基于用户数据文件正常启动 mongoDB ,从而确保回档成功。

腾讯云 MongoDB 优化后的物理备份回档直接带来了显著的性能提升:

备份耗时:减少70%

回档耗时:减少70%

存储成本:节省70%

网络带宽占用:减少70%

1 腾讯云物理备份回档原理

腾讯云 MongoDB 物理备份基于 wiredTiger 存储引擎的 hotbackup 功能实现,该功能底层以 checkpoint 检测点实现节点的完整数据快照。物理备份期间,会通过物理拷贝的方式拷贝整个数据目录中的所有文件存入腾讯云 cos 对象存储,数据文件越大,整个全量备份耗时就会越长。

同理,当对整个实例进行整实例回档的时候,我们需要从 cos 下载前面备份的所有 wt 数据文件,总文件越大,下载也会越耗时。

从上面的备份和回档流程可以看出,全量物理备份的时候我们需要拷贝 oplog.wt 到 cos , 下载的时候也需要从 cos 下载这个 oplog.wt 文件。

Oplog.wt 物理文件中存储的是历史增量数据,主要用于主从数据同步和增量逻辑备份。回档从 cos 下载完全量物理备份数据到3个节点,然后通过这3个节点构建副本集一致状态,这些用户数据表 .wt+ 其他内部表 .wt 可以决定这个全量一致状态,因此 oplog.wt 对构建副本集是无用数据。

此外腾讯云 MongoDB 团队也会逻辑实时备份 oplog.rs 表中的增量数据到 cos 对象存储,通过上面实例构建完的一致状态 +oplog.rs 增量数据的逻辑回放可以保证快速恢复数据到任意时间点。通过 oplog.rs 表实时逻辑备份的数据实际上和物理 oplog.wt 文件中的内容是重复的,oplog.rs 逻辑备份的数据包含了 oplog.wt 中的数据,因此再一次证明 oplog.wt 是重复的无用wt文件。

说明:为了便于理解,这里直接用表名 .wt 来表示该表对应磁盘上的 wt 文件,实际上表对应的 wt 文件名在 mongo server 层做了一次转换。实际对应关系可以通过 db. 表名 .stats().wi redTiger.uri 获取,以 oplog.wt 为例,它对应磁盘的文件路径如下:

本文基于以上背景,通过 MongoDB 内核支持忽略 oplog.wt 的备份和回档,从而让该游戏核心客户的全量备份回档时间提速70%。

2 wiredTiger 存储引擎数据持久化及恢复原理

wiredTiger 存储引擎的 hotbackup 功能以 wt 的 checkpoint 检测点为基础。单节点故障恢复除了依赖 checkpoint ,同时也依赖 WAL 。

2.1 WAL

先简单了解下 WAL 的一些理论说明, WAL(预写日志系统,( Write-ahead logging ,缩写 WAL ))是数据库管理系统**实现事务持久性与故障恢复的核心机制,通过"先记录日志后修改数据"的原则确保事务原子性和数据一致性。

2.1.1 为什么需要 WAL

假设以下场景:当用户写数据到 MongoDB 时内存很充足,所有的 KV 数据会存储在 wiredTiger 存储引擎的 B+ tree 中,写入10条普通 KV 数据后,因此 wt 的 dirty 指标很低,因此不会触发 B+ tree 的 page evict 操作,写入内存后就会返回客户端OK ,此时 B+ tree 的数据全部在内存中,还没有持久化。

如果这时候机器故障或者 mongod 故障节点挂掉了,重启节点后,由于数据没有持久化到磁盘,因此重启后数据就会丢失,如下图。

基于以上背景,因此就有了 WAL 模块的存在,在数据写入到 B+ tree 前,会提前写 WAL 日志,每个写事物对应一个完整 wal 日志,这样当服务器异常重启或者进程重启的时候,就可以通过 WAL 顺序回放这些日志完成数据恢复。Mongod 的 WAL 数据存储在 journal 目录中:

2.1.2 如果只有 WAL 会有什么问题

假设只依赖 wal 进行异常数据恢复,将存在以下问题:

● 数据恢复慢

Wal 的恢复流程:顺序读取每一个 WiredTigerLog.xx 文件,然后读取文件中的内容一行一行进行回放,如果 journal 目录中 WiredTigerLog.xx 文件很多,那么恢复速度就会越慢。

● 数据放大严重

假设我们只有一条数据,并且对这条数据 update 了几亿次操作,那么 wal 文件中会记录几亿次变更过程,磁盘占用空间会膨胀几亿倍。

● 消耗大量文件句柄

Wiredtiger 默认 WiredTigerLog.xx 文件大小100M,上T数据将用掉上万个文件句柄。

2.2 Checkpoint

为了解决 WAL 的以上痛点,因此引入 checkpoint 机制,MongoDB 的 checkpoint 模块功能主要由 wiredTiger 存储引擎完成。

2.2.1 Checkpoint 核心原理

Checkpoint 主要把当前 B+ tree 中所有可见脏 page 持久化到磁盘,从而实现一个完整数据快照。下面以一个完整的 checkpoint 流程为例进行说明(说明:为了简化,这里用一个方框来代表一个 page ,实际 B+ tree 这里还涉及ref相关数据结构):

如上图所示,假设当前用户写入数据量不大,并且没有触发 evict** ,那么所有用户page 数据都在内存中。当做 checkpoint 的时候,checkpoint 线程会遍历整个 b+ tree 中的可见 page,由于之前这棵 tree 中的所有 page 都没有持久化过,因此对 checkpoint 线程来说这些 page 都是脏 page 。持久化到磁盘后的状态大体如下:

第一次 checkpoint 完成后,假设更新了 leaf_page_1 中的数据,然后 checkpoint 线程进行第二次 checkpoint 操作,这时候的数据分别状态如下图:

最终两次 checkpoint 后,collection.wt 中包含了两次 checkpoint 的全部用户数据和 ext 元数据,每个 checkpoint 都对应一棵完整的 b+ tree,假设第一次 checkpoint 对应 checkpoint_1,第二次 checkpoint 对应 checkpoint_2, 则:

Checkpoint_1 = leaf_page_1 + leaf_page_2 + internal_page_1 + leaf_page_3 + leaf_page_4 + internal_page_2 +root_page

Checkpoint_2 = leaf_page_1(new) + leaf_page_2 + internal_page_1(new) + leaf_page_3 + leaf_page_4 + internal_page_2 +root_page(new)

当节点重启后,首先根据 WiredTiger.wt 元数据获取下图中的 checkpoint 信息,解析 checkpint 信息就可以获取到磁盘上的 root page,然后就可以逐层获取到整颗b+ tree 的所有 page。root page 就是通过 wiredTiger 启动时候解析WiredTiger.wt 文件中的 checkpoint 信息地址获取,如下:

这里 checkpoint=(WiredTigerCheckpoint.1=

(addr="018181e4c6ff67d88281e41546bd168381e48372ae35808080e22fc0cfc0")的 addr 是编码后的字符串,通过解码这个 addr 字符串即可解析出 root page 在磁盘中的位置以及其他 ext meta,从而可以在内存中重新构建该 b+ tree。Checkpoint 解析后的内容如下:

wiredTiger 相关 checkpoint 配置主要有下面两个参数:

● Checkpoint.log_size

当上次 checkpoint 后,wal的log_size 增加到配置的指定大小,则会触发wiredTiger 的 checkpoint server 线程做 checkpoint 操作。

● Checkpoint.wait

如果长时间没有满足 log_size 的大小,当离上次 checkpoint 时间达到 wait 值,也会触发 checkpoint server 线程做 checkpoint 操作。

MongoDB 没有启用 wiredTiger 自带的 checkpoint server

线程做 checkpoint 操作,而是 Mongo server 层创建了一个 checkpoint 线程定期进行 checkpoint 操作,checkpoint 周期默认60秒,可以通过修改配置文件 storage.syncPeriodSecs参数进行修改。

2.2.2 Checkpoint + WAL 结合优势

前面的 checkpoint 分析可以看出,节点重启的时候可以恢复到上一次开始做checkpoint 时候的全量数据快照,然后从这个 checkpoint 时间点回放对应 WAL 文件中的日志即可恢复数据到节点异常时间点,如下图所示:

一次 checkpoint 完成后,本次 checkpoint 时间点之前的 WAL 已经没用了,因此可以直接删除。最终节点重启后的数据恢复以最近一次 checkpoint 全量快照+checkpoint 时间点以后的 WAL 文件来恢复单机数据,其中:

● 全量数据

通过 checkpoint 时间点的快照,通过 WiredTiger.wt 元数据文件中的 checkpoint=(WiredTigerCheckpoint.1=(addr="018181e4c6ff67d88281e41546bd168381e48372ae35808080e22fc0cfc0")即可获取 checkpoint 开始时间点的全量数据。

● 增量数据

增量数据可以通过 checkpoint 时间点对应的 WAL 文件回放来完成,也就是图中的 WiredTigerLog.n-1 和 WiredTigerLog.n。

通过 checkpoint+WAL 的结合,彻底解决了之前只有 WAL 设计时候的恢复慢、数据量放大、句柄耗光等问题。

3 wiredTiger 存储引擎 hotbackup 核心原理及其优化

3.1 wiredTiger 存储引擎 hotbackup 核心原理

hotbackup 主要是完成物理 wt 文件的完整一致性拷贝,每个表都有自己的checkpoint.addr (这里面保存了 root 及其他 ext meta )信息,这些信息存储在WiredTiger.wt 元数据文件中。

通过 WiredTiger.wt 中的 checkpoint.addr 信息可以逐步在恢复整个 B+ tree 层级结构。除了这个关键信息外,这里还涉及到很多其他的元数据配置,例如创建表的时候可以指定以下参数:

创建表的时候,如果修改了 type、prefix_compression 和 value_format 的默认配置,则 wiredTiger.wt 中会保存这些表的元数据,这些元数据必须和创建表时候保持一致,否则节点重启会失败。

此外,除了上面的元数据外,还需要备份 schema 配置元数据,并存储表名和这些元数据配置的对应关系。所有这些元数据配置完全一致后,就可以通过元数据加载表对应 wt 文件,然后从元数据文件中的 checkpoint.addr 信息读取 root 地址,最终用户数据也就构建到 B+ tree 结构中。

整个 backup 代码实现主要分为三个阶段:

● backup cursor 生成阶段

这个步骤主要完成 schema 配置生成、确定需要拷贝的物理文件加入 cursor** 的 list buffer、设置 backup 标识等。

● backup cursor 遍历阶段

这个步骤主要告知我们需要拷贝哪些文件,通过 backup cursor 确定需要拷贝的文件名。

lbackup cursor 结束阶段

这个阶段主要包括资源回收,清理 backup 标识等。

3.1.1 backup cursor 生成阶段(也可以称为 begin 阶段)

这一阶段也叫 begin 阶段,begin 开始后会立马设置 backup 标识,标明当前正在做backup 操作。

该阶段会从 WiredTiger.wt 中读取所有" file:"相关的 wt 文件列表,这些列表对应具体的 wt 文件,也就是需要拷贝到 cos 对象存储的wt文件,被存储到 cursor list 数组中。

List 是指针结构,指向的内容为数组,数组的每一个 elem 代表一个需要拷贝的 wt 文件。然后获取这些 wt 文件的元数据配置信息存入"WiredTiger.backup.tmp"文件中,当所有的 wt 文件 schemap 配置及元数据写入 tmp 文件完成后,会 rename 更名为"WiredTiger.backup,最终"WiredTiger.backup"内容大体如下:

3.1.2 backup cursor 遍历阶段(也可以称为wt文件数据拷贝阶段)

这一阶段遍历 list[]数组即可获取 list[]数组中的每一个 elem,也就是可以获取每一个需要备份的 wt 物理文件。

获取到需要备份的 wt 文件后,旁路服务就可以并发拷贝 wt 文件存储到腾讯云 cos 对象存储中(包括 WiredTiger.backup 文件)。

3.1.3 backup cursor 遍历阶段(也可以称为 wt 文件数据拷贝阶段)

当物理文件拷贝完成后,backup cursor 即可 close 完成资源释放,同时清理backup 标识。

3.2 backup 数据恢复主要实现

从 cos 下载备份的 wt 文件后,wt 加载流程主要如下:

数据恢复实现主要是读取 WiredTiger.backup 内容,然后根据这些内容生成WiredTiger.wt 元数据文件,元数据文件生成完毕后就可以开始正式数据加载,mongod 即可通过 WiredTiger.wt 元数据文件获取每个表的配置信息,从而完成 wt 加载启动工作。

3.3 存储引擎 backup 剔除指定 wt 文件核心实现及其验证

3.3.1 backup 剔除指定 wt 文件实现

从上面的备份和恢复方式可以看出,WiredTiger.backup 中的内容是节点恢复的核心关键元数据,wt 启动所需的 WiredTiger.wt 中的 schema 配置和元数据内容全部来自 WiredTiger.backup 文件,而 WiredTiger.backup 文件在 begin 阶段完成,因此可以在 wt 的 backup 接口中增加了一个 exclude_target 参数,这个参数的作用主要是在 begin 阶段排除指定 wt 文件。假设剔除的表为 collection1, Hotback begin 阶段剔除 collection1 表后,WiredTiger.backup 文件中的内容将不再包含 collection1 的相关内容(这里腾讯云自己对 wiredTiger 做了修改),如下图:

同理,当 cos 下载 wt 文件恢复数据的时候,也就不会下载 collection.wt 文件,从而也就提升了回档下载耗时。

3.3.2 backup 剔除指定 wt 文件验证

为了验证 backup 新增的 exclude_target 功能是否能正常备份和回档,按照wiredTiger 存储引擎以下 demo 步骤进行验证,第三步和第四步如果验证通过,则说明 backup 剔除指定 collection 功能实现没有问题:

第一步:数据准备

新建10个表,每个表写100条数据,然后主动执行一次 checkpoint。

第二步:exclude_target 剔除其中一个表(只物理备份9个表)进行数据备份

调用修改后的 backup 功能,生成备份数据。

第三步:加载备份数据

用第二步生成的备份数据,加载 wt 文件,确定是否可以加载成功。

第四步:验证备份数据正确性

验证备份的9个表中的内容是否和备份前的内容完全一致。

● 准备数据

数据准备阶段创建10个表,每个表写入100条数据,相关核心 demo 代码如下:

● exclude_target 剔除其中一个表(只备份9个表)进行数据备份

上一步创建了10个表,新增 exclude_target 参数功能后,这里 backup 备份剔除其中一个表(collection_0), 因此 cursor next 只会获取到9个表,主要 demo如下:

这个 demo 在 BACKUP_DIR 中的备份文件只会有9个,也就是 collection_1 -- collection_9,符合预期。

● 加载 backup 的备份数据

这里验证通过,每个步骤不会报错,数据可以正常加载, 验证通过。

● 验证备份数据正确性

这里验证通过,每个步骤不会报错,数据可以正常加载,并且能打印出collection_1-collection_9 这9个表中的KV数据,打印的内容与写入表中的内容完全一致。

从上面的 demo 验证可以确认,忽略指定 wt 备份恢复验证通过,说明从存储引擎一侧验证该方案没问题。

4 MongoDB server 层适配

MongoDB 服务器本身依赖一些元数据文件来管理所有数据表,如果直接去掉 oplog.rs 文件后,启动时会因找不到该文件而报错。MongoDB server 层同样通过 backup cursor(带上新增的 exclude_target=oplog.wt)进行适配。

Server 层适配的时候可以先获取 oplog.rs 对应的磁盘 wt 文件名,然后加入exclude_target 参数中,获取 oplog.rs 对应磁盘 wt 文件名可以通过db.serverstatus().wiredTiger.uri 这个接口获取,server 层 backup 接口如下:

Server 适配该新功能启动 mongod 节点,发现启动报错,无法找到 oplog.rs 对应的 wt 文件。通过走读代码,可以确认该问题是因为 mongod 启动的时候,会对 server 层管理的 wt 元数据做二次检查(主要是 _mdb_catalog.wt 和sizeStorer.wt),这两个 wt 文件存储的是 server 层用来维护表信息、索引信息以及每个表 count 数。

● _mdb_catalog.wt 文件中的数据内容

从上图可以看出,_mdb_catalog.wt 存储着表的元数据信息以及该表创建的索引信息,这些信息由 server 层写入 wiredtiger 引擎。

● sizeStorer.wt 文件中的数据内容

这里的 key 一一对应一个具体的表,每个 value 存储该表中的数据总大小以及数据条数,节点重启的时候,表的 count 就是从这个 wt 文件获取的。

由于 wiredTiger 引擎侧我们通过 exclude_target 删除了 oplog.rs 表对应的 wt文件,但是 mongo server 侧在 _mdb_catalog.wt 和 sizeStorer.wt 两个文件中维护着表、索引、数据量相关元数据。Mongod 启动的时候会加载这两个 wt 文件获取到对应的表,然后去检查这些表是否存在。由于 wiredTiger 引擎侧删除了oplog.rs 对应的 wt 文件,因此 mongo server 侧检查不通过。

腾讯云 MongoDB 内核除了在 wiredTiger 存储引擎侧通过 exclude_target 功能新增功能剔除了 oplog.rs 表,同时 server 侧修复了 mongo server 维护的oplog.rs 相关检查,最终解决了 wiredtiger 引擎测和 mongo server 测启动失败的问题。

5 结语

腾讯云 MongoDB 通过打造有特色的产品功能、进行深度的内核优化和创新,以及提供更极致的服务,逐步构建起自身的差异化优势,致力于为用户带来了业界顶级的的数据库性能和稳定性,未来,腾讯云 MongoDB 也将在不断提升产品性能与可靠性的同时,切实为客户带来更优的经济效益与更流畅的运维体验。

相关推荐
正在走向自律35 分钟前
金仓数据库引领新能源行业数字化转型:案例深度解析与领导力展现
数据库·国产数据库·kingbasees·金仓数据库·电科金仓·新能源行业
key_Go1 小时前
3-2.SQL语言(续)
数据库·mysql
电商API_180079052472 小时前
淘宝详情数据 API 返回字段全解析:核心字段说明 + 开发避坑指南
大数据·数据库·性能优化·数据挖掘·数据分析·网络爬虫
倔强的石头1062 小时前
从海量时序数据到无人值守:数据库在新能源集控系统中的架构实践
数据库·架构·金仓数据库
lqj_本人2 小时前
鸿蒙Qt数据库实战:SQLite死锁与沙箱路径陷阱
数据库·qt·harmonyos
罗光记2 小时前
低空基础设施新突破!优刻得 ×IDEA联合发布 OpenSILAS一体机
数据库·经验分享·其他·百度·facebook
合作小小程序员小小店2 小时前
web网页开发,在线%餐饮点餐%系统,基于Idea,html,css,jQuery,java,ssm,mysql。
java·前端·数据库·html·intellij-idea·springboot
p***43482 小时前
SQL在业务智能中的分析函数
数据库·sql
j***29483 小时前
【MySQL — 数据库基础】深入理解数据库服务与数据库关系、MySQL连接创建、客户端工具及架构解析
数据库·mysql·架构