bigywater.tech
MongoDB · 80

mongodump --oplog 与 mongorestore 的一致性

在线备份时数据还在写,mongodump 出来的东西能恢复成一个一致的状态吗?--oplog 和 --oplogReplay 到底做了什么?这篇用一个 n == 文档数的不变式逐个验证。

−623
普通 dump 恢复后 a_counter.n 与 c_w 文档数之差
1,254
--oplogReplay 重放的操作数,恢复后 n == w
−619
带 oplog dump 但不带 --oplogReplay 恢复的差值

普通 dump 不一致

实测 · 本机执行输出
  $ mongodump --uri='<副本集>' --db=test --out=/data/m72dump/plain
  writing `test.c_w` to `/data/m72dump/plain/test/c_w.bson`
  writing `test.a_counter` to `/data/m72dump/plain/test/a_counter.bson`
  done dumping `test.a_counter` (1 document)
  done dumping `test.c_w` (150824 documents)
  $ mongorestore --uri='<目标>' --drop --nsInclude='test.*' /data/m72dump/plain
  finished restoring `test.a_counter` (1 document, 0 failures)
  finished restoring `test.c_w` (150824 documents, 0 failures)
  150825 document(s) restored successfully. 0 document(s) failed to restore.
  普通 dump 恢复后:a_counter.n = 150,201,c_w 文档数 = 150,824 → 不一致,n - w = -623

写入进行中做普通 mongodump,各集合在不同时间被读取,恢复后 a_counter.n 与 c_w 的文档数差了 623。

oplog.bson

实测 · 本机执行输出
  $ mongodump --uri='<副本集>' --oplog --out=/data/m72dump/withoplog
  writing `admin.system.version` to `/data/m72dump/withoplog/admin/system.version.bson`
  done dumping `admin.system.version` (1 document)
  writing `test.c_w` to `/data/m72dump/withoplog/test/c_w.bson`
  writing `test.a_counter` to `/data/m72dump/withoplog/test/a_counter.bson`
  done dumping `test.a_counter` (1 document)
  done dumping `test.c_w` (151656 documents)
  writing captured oplog to ``
  dumped 627 oplog entries
  $ ls dump/ dump/test/
  admin oplog.bson prelude.json test  |  test/: a_counter.bson a_counter.metadata.json c_w.bson c_w.metadata.json
  oplog.bson 里有 627 条 oplog 记录({'c:applyOps': 627}),时间戳从 1791782833:109 到 1791782837:77,其中对 test.c_w 的 insert 共 627 个、对 test.a_counter 的 update 共 627 个
  oplog.bson 第一条记录里的操作(applyOps 展开,ns / op / o / o2): [{"op": "i", "ns": "test.c_w", "o": {"_id": 1035}, "o2": {"_id": 1035}}, {"op": "u", "ns": "test.a_counter", "o": {"$v": 2, "diff": {"u": {"n": 151036}}}, "o2": {"_id": 1}}]

--oplog 让 mongodump 在结束时把 dump 期间的 oplog 写进 oplog.bson;事务提交在里面是 applyOps,update 记的是新值。

--oplogReplay

实测 · 本机执行输出
  $ mongorestore --uri='<目标>' --drop --oplogReplay /data/m72dump/withoplog
  don't know what to do with file `/data/m72dump/withoplog/prelude.json`, skipping...
  reading metadata for `test.a_counter` from `/data/m72dump/withoplog/test/a_counter.metadata.json`
  reading metadata for `test.c_w` from `/data/m72dump/withoplog/test/c_w.metadata.json`
  restoring `test.a_counter` from `/data/m72dump/withoplog/test/a_counter.bson`
  finished restoring `test.a_counter` (1 document, 0 failures)
  restoring `test.c_w` from `/data/m72dump/withoplog/test/c_w.bson`
  finished restoring `test.c_w` (151656 documents, 0 failures)
  replaying oplog
  applied 1254 oplog entries
  --oplogReplay 恢复后:a_counter.n = 151,662,c_w 文档数 = 151,662 → 一致(n == w)

恢复时带 --oplogReplay,mongorestore 导完集合后重放 oplog,1254 个操作后两个集合一致。

忘了 --oplogReplay

实测 · 本机执行输出
  $ mongorestore --uri='<目标>' --drop /data/m72dump/withoplog
  don't know what to do with file `/data/m72dump/withoplog/prelude.json`, skipping...
  reading metadata for `test.a_counter` from `/data/m72dump/withoplog/test/a_counter.metadata.json`
  reading metadata for `test.c_w` from `/data/m72dump/withoplog/test/c_w.metadata.json`
  restoring `test.c_w` from `/data/m72dump/withoplog/test/c_w.bson`
  restoring `test.a_counter` from `/data/m72dump/withoplog/test/a_counter.bson`
  finished restoring `test.a_counter` (1 document, 0 failures)
  finished restoring `test.c_w` (151656 documents, 0 failures)
  不带 --oplogReplay 恢复后:a_counter.n = 151,037,c_w 文档数 = 151,656 → 不一致,n - w = -619

不带 --oplogReplay 恢复,oplog.bson 被忽略且没有任何提示,结果和普通 dump 一样不一致。

--oplogLimit

实测 · 本机执行输出
  选取的时间戳 1791782835:78(oplog.bson 第 314/627 条)
  $ mongorestore --uri='<目标>' --drop --oplogReplay --oplogLimit=1791782835:78 /data/m72dump/withoplog
  don't know what to do with file `/data/m72dump/withoplog/prelude.json`, skipping...
  reading metadata for `test.a_counter` from `/data/m72dump/withoplog/test/a_counter.metadata.json`
  reading metadata for `test.c_w` from `/data/m72dump/withoplog/test/c_w.metadata.json`
  restoring `test.c_w` from `/data/m72dump/withoplog/test/c_w.bson`
  restoring `test.a_counter` from `/data/m72dump/withoplog/test/a_counter.bson`
  finished restoring `test.a_counter` (1 document, 0 failures)
  finished restoring `test.c_w` (151656 documents, 0 failures)
  replaying oplog
  applied 626 oplog entries
  --oplogLimit 恢复后:a_counter.n = 151,348,c_w 文档数 = 151,656 → 不一致,n - w = -308
  对比完整重放:n = 151,662、c_w = 151,662
  $ mongorestore --uri='<目标>' --drop --oplogReplay --oplogLimit=1791782837:77 /data/m72dump/withoplog
  applied 1252 oplog entries
  --oplogLimit = 最后一条记录的时间戳:a_counter.n = 151,661,c_w 文档数 = 151,661 → 一致(n == w)
  $ mongorestore --uri='<目标>' --drop --oplogReplay --oplogLimit=1791782837:78 /data/m72dump/withoplog
  applied 1254 oplog entries
  --oplogLimit = 最后一条记录的时间戳 + 1:a_counter.n = 151,662,c_w 文档数 = 151,662 → 一致(n == w)

--oplogLimit 取 dump 窗口中间的时间戳会得到不一致的状态(n − w = −308);取窗口末尾才一致,limit 不含时间戳本身。

使用限制

实测 · 本机执行输出
  $ mongodump --uri='<副本集>' --oplog --db=test --out=/data/m72dump/bad1
  Failed: bad option: --oplog mode only supported on full dumps
  $ mongodump --uri='<目标>' --oplog --out=/data/m72dump/bad2
  Failed: error getting oplog start: error getting recent oplog entry: mongo: no documents in result
  $ mongorestore --uri='<目标>' --oplogReplay --db=test /data/m72dump/withoplog/test
  The --db and --collection flags are deprecated for this use-case; please use --nsInclude instead, i.e. with --nsInclude=${DATABASE}.${COLLECTION}
  Failed: cannot use --oplogReplay with includes specified
  0 document(s) restored successfully. 0 document(s) failed to restore.

--oplog 只支持整库 dump、需要有 oplog 的副本集;--oplogReplay 不能和 --db/includes 一起用。

分步演示

mongodump --oplog 与恢复
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / MongoDB 9.0.2(Homebrew 的 mongod、mongos)/ mongosh 2.12.0 / pymongo 4.18.3;每个实验在临时目录里起全新的 mongod(独立 dbpath,端口 9900 以上,只监听 127.0.0.1,WiredTiger 缓存 0.25 GB),需要副本集或分片集群时在本机起多个进程;命令输出来自真实的 mongosh,批量写入、计时与多线程用 pymongo;计时用 time.perf_counter,数字每次运行会有小幅抖动,ObjectId、时间戳等每次运行都不同(mongodump/mongorestore 100.16.1)。3 节点副本集做源,单机 mongod 做恢复目标;一个事务循环在 dump 期间持续写入
    • 没有验证:分片集群的备份(mongodump 对 mongos 的 --oplog 行为);dump 窗口里发生 DDL(建索引、drop)时的重放;大事务被拆成多条 applyOps 时的重放;oplog 窗口被覆盖时 mongodump 的报错;--oplogLimit 在连续 oplog 备份上做时间点恢复的完整流程;不同 mongodump 版本的差异;--numParallelCollections 的影响
    京ICP备2026067502号-1京公网安备11010502063487号