要实现数据库增量备份一般是基于物理备份来实现,比如PostgreSQL的连续归档备份。但是MongoDB官方没有提供物理备份工具,只提供了逻辑备份工具:mongoexport和mongodump,而逻辑备份是一般是无法实现增量备份。
但是MongoDB副本集架构中,提供了Oplog集合来存储所有的数据变更操作,并且具有幂等性,可以利用它来做数据增量备份和恢复。
oplog是什么
Oplog是副本集架构下local库下的一个固定集合oplog.rs,Oplog存储了MongoDB数据库所发生的操作记录。MongoDB主节点和从节点之间数据同步就是基于oplog来做的。当Primary进行写操作的时候,会将这些写操作记录写入Primary的Oplog中,而后Secondary会将Oplog复制到本机,并应用这些操作。
Oplog集合本身是一个Capped Table(固定大小集合),它只能保存特定数量的操作日志,通常oplog使用空间的增长速度跟系统处理写请求的速度相当。
可以通过rs.printReplicationInfo()可以用来查看oplog的大小,操作日志的起始时间。
replSet1:PRIMARY> db.getReplicationInfo()
{
"logSizeMB" : 2048,
"usedMB" : 2046.72,
"timeDiff" : 35306150,
"timeDiffHours" : 9807.26,
"tFirst" : "Thu Sep 17 2020 01:33:48 GMT+0800 (CST)",
"tLast" : "Sat Oct 30 2021 16:49:38 GMT+0800 (CST)",
"now" : "Sat Oct 30 2021 16:49:46 GMT+0800 (CST)"
}
字段说明:
configured oplog size:oplog文件大小,单位是MB;
log length start to end: oplog日志的启用时间段;
oplog first event time: 第一个事务日志的产生时间;
oplog last event time: 最后一个事务日志的产生时间;
now: 现在的时间;
也可以使用db.getReplicationInfo()来查看oplog的状态、大小、存储的时间范围。
replSet1:PRIMARY> rs.printReplicationInfo()
configured oplog size: 2048MB
log length start to end: 35306860secs (9807.46hrs)
oplog first event time: Thu Sep 17 2020 01:33:48 GMT+0800 (CST)
oplog last event time: Sat Oct 30 2021 17:01:28 GMT+0800 (CST)
now: Sat Oct 30 2021 17:01:30 GMT+0800 (CST)
oplog各字段含义
Capped collections可以使用$natural操作符按插入顺序的正序或反序返回结果:
use local
db.oplog.rs.find({}).sort({$natural: -1}).limit(1)
我们使用上述命令查询最新一条oplog文档。
replSet1:PRIMARY> db.oplog.rs.find({}).sort({$natural: -1}).limit(1).pretty()
{
"ts" : Timestamp(1635509740, 2),
"t" : NumberLong(78),
"h" : NumberLong(0),
"v" : 2,
"op" : "u",
"ns" : "zz.a_test",
"ui" : UUID("560a8f7e-f813-48dd-9d82-66b0f12397f0"),
"o2" : {
"_id" : ObjectId("60df16788d251d0010445c1c")
},
"wall" : ISODate("2021-10-29T12:15:40.267Z"),
"o" : {
"_id" : ObjectId("60df16788d251d0010445c1c"),
"bizSpace" : "cs",
"projectKey" : "1000",
"functionPointName" : "编辑",
"pid" : "60df16788d251d0010445c19",
"position" : 3,
"permissionKey" : "agentAccount:update",
"hasDataPermission" : false,
"isDelete" : NumberLong(0),
"gmtCreate" : ISODate("2021-07-02T13:36:56.412Z"),
"gmtModified" : ISODate("2021-07-02T13:36:56.412Z")
}
}
字段含义:
ts: 操作的时间戳,64位表示,高32位是时间戳,低32位是计数累加。 t: election term。对应raft协议里面的term,每次发生节点down掉,新节点加入,主从切换,term都会自增。 h: 操作的全局唯一id的hash结果。 v: oplog的版本字段。 op: 具体操作类型。"i"表示插入,"d"表示删除,"u"表示更新,"c"表示是DDL操作,"n"表示是空消息(可以看作是心跳oplog)。 ns: 命名空间。操作发生在哪个表上面。 o: 具体的操作指令字段。对于op=i,这里表示插入的内容;op=d,这里表示要删除的文档;op=u,这里表示更新后的内容。 o2: 查询字段,只存在于op=u的情况,指定要操作的文档。
oplog增量备份
所谓备份即意味着转储,而增量备份(incremental backup)是备份的一个类型,是指在一次全备份或上一次增量备份后,以后每次的备份只需备份与前一次相比增加或者被修改的部分。
前面我们说到oplog是一个集合,那么我们可以使用mysqldump对其进行备份。
mysqldump命令
语法:
mongodump -h IP --port 端口 -u 用户名 -p 密码 -d 数据库 -c 集合 -o 文件存在路径
其中:
--db
--collection
--gzip:压缩备份文件。压缩输出,如果mongodump指定导出到目录,则该选项会将每个文件都压缩,并添加.gz后缀。如果mongodump指定导出到文档或标准输出流,则该选项会压缩到文档或输出流中。
--out
--archive
开始增量备份:
1)首先我们需要上一次全备或增量备份后的时间戳,以确定增量备份的开始时间。
问题是怎么获取这个时间戳呢?
假如是全备结束,我们想看全备结束时的时间戳,可以给mongodump设置参数--oplog,该参数会在备份路径下产生一个oplog.bson文件,里面存放了开始进行dump到dump结束之间所有的op log操作,如下图所示:

图片来源:https://www.w3xue.com/exp/article/201811/7362.html
mongodump -u root -p root -o /tmp/mongo_dump --oplog
oplog.bson文件是bson格式的,没有办法直接查看,打开是乱码,我们可以使用bsondump将bson转化为json格式。
./bsondump /tmp/mongo_dump/oplog.bson
{"ts":{"$timestamp":{"t":1635651513,"i":1}},"t":{"$numberLong":"28"},"h":{"$numberLong":"-611837201367149992"},"v":2,"op":"n","ns":"","wall":{"$date":"2021-10-31T03:38:33.705Z"},"o":{"msg":"periodic noop"}}
得到的时间戳是:{"ts":{"$timestamp":{"t":1635651513,"i":1}}
2)导出新的oplog(增量)。
我们已经知道了上次全备后的时间戳,那么我们就可以使用条件,{ts:{$gt: Timestamp(1635651513, 1)}},导出自上次备份结束,截止到目前的所有oplog记录。
mongodump -u root -p root -d local -c oplog.rs -q '{ts:{$gt: Timestamp(1635651513, 1)}}' -o /tmp/mongo_oplogBack --authenticationDatabase admin
2021-10-31T11:52:29.847+0800 writing local.oplog.rs to
2021-10-31T11:52:29.902+0800 done dumping local.oplog.rs (83 documents)
注:--q指定的时间戳可以比全量开始时间提前几秒,以防止数据丢失,因为oplog的幂等性,这并不影响MongoDB恢复数据。
看下增量备份生成的文件。
ls /tmp/mongo_oplogBack/
local
ls /tmp/mongo_oplogBack/local
oplog.rs.bson oplog.rs.metadata.json
此处的oplog.rs.bson可以基本类比于全备过程中产生的oplog.bson,我们也可以使用bsondump来解析这个文件,获取这次增量备份后的时间戳。
3)增量恢复。
拿到了oplog的增量备份数据,我们就可以结合mongorestore工具,进行增量恢复。
mongorestore --host xxx --port xxx --oplogReplay /tmp/mongo_oplogBack/local/oplog.rs.bson
线上业务不断有写入,也即oplog是不断的追加的,但是oplog大小是固定的。如果想让目标端与源端保持数据同步就需要对oplog进行持续的增量备份,或者如果想做按时间点恢复,也需要保证是oplog是连续的。
这里有一个关键点,就是增量备份频率的设置。因为oplog的大小是固定的,很可能出现在增量备份间隔内,出现oplog被覆盖的情况,那么这时候备份的oplog数据就是不完整的,也将导致利用它恢复出的数据是不完整的。
所以我们需要评估业务的写入压力,比如单位时间内产生多少日志,来合理设置oplog的大小,如果系统磁盘充足,可以将oplog集合设置的大些。
参考文档:
https://tech.willhaben.at/mongodb-incremental-backups-dff4c8f54d58
https://www.w3xue.com/exp/article/201811/7362.html




