自在学

我们与你共同进步

  • 分类课程
  • 文章
  • 工作台
  • 订阅

  • 关于我们
  • 隐私政策
  • 使用条款

探索

  • 分类课程
  • 文章
  • 工作台
  • 订阅

网站信息

  • 关于我们
  • 隐私政策
  • 使用条款

加入社区

自在学学习社区微信二维码

微信扫码,交流学习

株洲市自在学教育科技有限公司© 2025 - 2026 版权所有

© 2025 - 2026 株洲市自在学教育科技有限公司 版权所有

湘公网安备43020302000292号|湘ICP备2025148919号-1
分类课程工作台文章订阅
分类课程工作台文章价格

MongoDB 纸舟书店完整课程

  1. 01MongoDB 与纸舟书店
  2. 02启动 MongoDB 8.0
  3. 03BSON、集合与纸舟书店种子数据
  4. 04用 CRUD 推进纸舟书店业务
  5. 05查询、投影、排序与稳定分页
  6. 06原子更新、批量写入与并发语义
  7. 07用访问模式设计纸舟书店的数据
  8. 08用索引和 explain 量化查询成本
  9. 09用聚合管道回答纸舟书店的业务问题
  10. 10用 Node.js 22 连接纸舟书店
  11. 11分片、分片键与水平扩展
  12. 12纸舟书店的完整交付
  13. 13监控、慢查询与性能诊断
  14. 14副本集、故障转移与一致性
  15. 15从单文档原子性到事务下单
  16. 16备份、恢复与恢复验收
  17. 17认证、授权与安全边界
正在加载课程章节内容
课程编程MongoDB 纸舟书店完整课程备份、恢复与恢复验收

备份、恢复与恢复验收

复制能让另一个成员接替故障节点,却会忠实复制误删除和错误更新。备份保存的是可回到过去的恢复材料;只有把材料恢复出来并通过验收,才算拥有可用的备份。本节会把纸舟书店归档,恢复到另一个数据库,并检查文档、索引和验证规则。


知识点:先区分复制、备份与导出

这三个词经常被放在一起,但用途不同:

能力主要目标典型工具是否保留 BSON 类型与索引元数据
副本集节点故障后继续服务oplog 与选举是,但错误操作也会复制
逻辑备份保存可恢复的数据库快照mongodump / mongorestore保留 BSON;可恢复集合选项和索引
数据导出与其他系统交换可读数据mongoexport / mongoimportJSON/CSV 可能丢失类型或数据库元数据

mongoexport 生成 JSON 并不等于完整备份。订单中的日期、Decimal128、ObjectId、索引和 schema validation 都需要额外处理。恢复 MongoDB 数据库时,应优先使用与目标相符的备份产品或 Database Tools。

MongoDB Database Tools 独立于服务器发布。课程使用的镜像中可以分别查看版本:

shell
docker exec paperboat-mongo mongod --version | head -n 1
docker exec paperboat-mongo mongodump --version | head -n 1
text
db version v8.0.26
mongodump version: 100.17.0

具体补丁号会随镜像更新时间变化。关键是记录服务器、工具、备份时间、来源拓扑和恢复要求,而不是假设所有版本都能任意互换。官方说明建议让恢复目标使用与来源相同的主版本或相同的 feature compatibility version。


知识点:RPO 和 RTO 决定备份方案

两个指标能把“多备份一点”变成可验证要求:

  • RPO(恢复点目标):最多可以丢失多长时间的数据。每天一次备份通常意味着最坏可能损失接近一天的变化。
  • RTO(恢复时间目标):故障后允许多长时间恢复服务。备份文件存在,但下载、解密和恢复需要六小时,就无法满足一小时的 RTO。

小型项目可以从定时逻辑备份开始。订单量和 RPO 要求提高后,应评估 Atlas 连续备份、文件系统快照、存储快照或能提供时间点恢复的方案。选择工具之前,先写清 RPO、RTO、数据规模、拓扑和合规要求。

mongodump 会读取业务数据,也会消耗 CPU、磁盘和网络。不要在高峰期未经评估地对大型生产库运行,更不要把它当作任何规模下都合适的唯一方案。


知识点:归档、压缩和校验各做一件事

本节使用三个选项:

  • --archive 把多个 BSON 与元数据流装进一个文件,便于传输。
  • --gzip 压缩归档,恢复时也必须带上 --gzip。
  • SHA-256 摘要用于发现文件在传输或保存后是否变化,它不提供加密。

备份文件可能包含全部业务数据,应按敏感数据管理:限制读取权限、在传输和静态存储时加密、设置保留周期,并让删除策略覆盖旧副本。


实操:生成纸舟书店归档

先查看即将备份的文档数量,建立恢复对照。下面只读取三个核心集合:

shell
docker exec paperboat-mongo mongosh --quiet --eval '
const d = db.getSiblingDB("bookstore");
printjson({
  books: d.books.countDocuments({}),
  customers: d.customers.countDocuments({}),
  orders: d.orders.countDocuments({})
})
'

结果展示:记录恢复前基线

在课程数据集上,结果是:

javascript
{
  books: 4,
  customers: 4,
  orders: 7
}

接着执行 mongodump。归档先写入容器内的 /tmp,避免不同镜像用户对共享目录权限不一致:

shell
docker exec paperboat-mongo sh -lc '
set -eu
rm -f /tmp/bookstore.archive
mongodump \
  --uri="mongodb://127.0.0.1:27017/?directConnection=true&replicaSet=rs0" \
  --db=bookstore \
  --archive=/tmp/bookstore.archive \
  --gzip
test -s /tmp/bookstore.archive
ls -lh /tmp/bookstore.archive
'

set -e 让任意命令失败时立即返回非零状态;test -s 要求归档存在且不为空。不要为了输出整洁而先加 --quiet,否则权限或路径错误更容易被忽略。

mongodump 会列出正在写入的集合,并以类似信息结束:

text
writing bookstore.books to archive '/tmp/bookstore.archive'
writing bookstore.customers to archive '/tmp/bookstore.archive'
writing bookstore.orders to archive '/tmp/bookstore.archive'
done dumping bookstore.books (4 documents)
done dumping bookstore.customers (4 documents)
done dumping bookstore.orders (7 documents)
-rw-r--r-- 1 root root 3.8K ... /tmp/bookstore.archive

文件大小会因索引、验证规则、压缩版本和文档内容变化,不应把 3.8K 当成固定验收值。真正的最低门槛是:命令退出码为零,而且归档非空。


实操:复制归档并生成摘要

现在把归档复制到项目的 backups 目录。文件名带 UTC 时间,避免新备份无意覆盖旧备份:

shell
mkdir -p backups
backup_file="backups/bookstore-$(date -u +%Y%m%dT%H%M%SZ).archive.gz"
docker cp paperboat-mongo:/tmp/bookstore.archive "$backup_file"
printf '%s\n' "$backup_file" > backups/latest-course-backup.txt
text
Successfully copied 5.63kB to .../backups/bookstore-20260715T083000Z.archive.gz

latest-course-backup.txt 记录本次课程归档的精确相对路径。这样即使关闭终端,恢复步骤也不依赖只存在于某个 shell 进程中的变量。

再计算摘要并保存在相邻文件中:

shell
if command -v shasum >/dev/null 2>&1; then
  shasum -a 256 "$backup_file" | tee "$backup_file.sha256"
else
  sha256sum "$backup_file" | tee "$backup_file.sha256"
fi
text
9f84b8...  backups/bookstore-20260715T083000Z.archive.gz

结果展示:验证文件和摘要

先列出文件,再让 shasum 重新计算并比对:

shell
ls -lh "$backup_file" "$backup_file.sha256"
text
-rw-r--r--  ... bookstore-20260715T083000Z.archive.gz
-rw-r--r--  ... bookstore-20260715T083000Z.archive.gz.sha256
shell
if command -v shasum >/dev/null 2>&1; then
  shasum -a 256 -c "$backup_file.sha256"
else
  sha256sum -c "$backup_file.sha256"
fi
text
backups/bookstore-20260715T083000Z.archive.gz: OK

OK 只表示当前文件与生成摘要时相同。macOS 通常提供 shasum,Linux 与 WSL 常见的是 sha256sum,条件分支会选择实际存在的工具。若攻击者能同时替换归档和摘要,普通哈希无法证明来源;更严格的流程需要受保护的签名、不可变存储和访问审计。


知识点:恢复演练应避开原数据库

第一次验证备份时,不要直接覆盖唯一的 bookstore。我们使用命名空间映射,把:

text
bookstore.books       → bookstore_restore.books
bookstore.customers   → bookstore_restore.customers
bookstore.orders      → bookstore_restore.orders

这样可以对照源和恢复结果。--drop 只删除目标归档中将要恢复的集合,避免上一次演练残留数据让计数虚高。


实操:恢复到隔离的数据库名

先从指针文件重新读取归档路径,并验证它仍是课程约定的文件名且文件非空,再复制回容器。这段命令可以在新的终端会话中独立执行:

shell
backup_file="$(cat backups/latest-course-backup.txt)"
case "$backup_file" in
  backups/bookstore-*.archive.gz) ;;
  *) echo "归档路径不符合课程约定" >&2; exit 1 ;;
esac
test -s "$backup_file"
test -s "$backup_file.sha256"
docker cp "$backup_file" paperboat-mongo:/tmp/bookstore-restore.archive
text
Successfully copied 5.63kB to paperboat-mongo:/tmp/bookstore-restore.archive

执行恢复,并把源命名空间重写到 bookstore_restore:

shell
docker exec paperboat-mongo mongorestore \
  --uri="mongodb://127.0.0.1:27017/?directConnection=true&replicaSet=rs0" \
  --archive=/tmp/bookstore-restore.archive \
  --gzip \
  --nsFrom='bookstore.*' \
  --nsTo='bookstore_restore.*' \
  --drop

结果展示:读取工具汇总

关键输出如下:

text
restoring bookstore_restore.books from archive
restoring bookstore_restore.customers from archive
restoring bookstore_restore.orders from archive
restoring indexes for collection bookstore_restore.books
restoring indexes for collection bookstore_restore.orders
15 document(s) restored successfully. 0 document(s) failed to restore.

15 正好等于基线中的 4 + 4 + 7,0 failed 表示工具没有报告文档恢复失败。但这仍不是最终验收:我们还要从数据库中读取恢复结果。


知识点:恢复验收至少有四层

一次有意义的恢复检查应覆盖:

  1. 数量:集合与文档数量是否合理。
  2. 内容:抽样或业务不变量是否成立。
  3. 结构:索引、唯一约束、validator 和集合选项是否存在。
  4. 应用:真实应用能否连接并完成关键读写流程。

只看 mongorestore 的退出码,无法发现备份时间不对、缺少业务集合或恢复了错误环境的数据。


实操:比较数量、索引和验证规则

下面同时读取源库与恢复库的数量:

shell
docker exec paperboat-mongo mongosh --quiet --eval '
const source = db.getSiblingDB("bookstore");
const restored = db.getSiblingDB("bookstore_restore");
 
printjson({
  source: {
    books: source.books.countDocuments({}),
    customers: source.customers.countDocuments({}),
    orders: source.orders.countDocuments({})
  },
  restored: {
    books: restored.books.countDocuments({}),
    customers: restored.customers.countDocuments({}),
    orders: restored.orders.countDocuments({})
  }
})
'

结果展示:数量完全对应

javascript
{
  source: { books: 4, customers: 4, orders: 7 },
  restored: { books: 4, customers: 4, orders: 7 }
}

接着检查索引名称和 orders 的 validator:

shell
docker exec paperboat-mongo mongosh --quiet --eval '
const d = db.getSiblingDB("bookstore_restore");
printjson({
  bookIndexes: d.books.getIndexes().map(i => i.name),
  customerIndexes: d.customers.getIndexes().map(i => i.name),
  orderIndexes: d.orders.getIndexes().map(i => i.name),
  hasOrderValidator:
    Boolean(d.getCollectionInfos({ name: "orders" })[0]?.options?.validator)
})
'
javascript
{
  bookIndexes: [
    '_id_',
    'category_1_price_1',
    'published_1_category_1_price_1_id_1',
    'published_1_price_1_id_1'
  ],
  customerIndexes: [
    '_id_',
    'email_unique'
  ],
  orderIndexes: [
    '_id_'
  ],
  hasOrderValidator: true
}

索引名称取决于前面章节实际创建的索引。验收时应把期望清单放进版本控制,并检查关键唯一索引、复合索引和 validator,而不只比较“索引数量大于零”。

最后抽查一条订单的金额不变量:

shell
docker exec paperboat-mongo mongosh --quiet --eval '
const order = db.getSiblingDB("bookstore_restore").orders.findOne(
  { _id: "order-1001" },
  { _id: 1, total: 1, status: 1, "items.quantity": 1 }
);
printjson(order)
'

输出应包含目标订单、状态和条目数量。实际项目还可以重新计算 items 小计、对账库存,或让 API 指向恢复库运行一组只读冒烟检查。


知识点:副本集备份需要一致时间点

对正在写入的副本集,连续读取多个集合时,数据可能在备份过程中变化。mongodump --oplog 可以在执行完整副本集成员备份时同时捕获期间的 oplog 变化,恢复时配合 --oplogReplay 获得更一致的时间点。

它有重要限制:--oplog 不能与只选择某个数据库或集合的选项随意组合,恢复工具版本和服务器版本也要匹配。大型生产系统还要考虑负载、备份窗口、分片集群协调和时间点恢复,不能把本节的单库归档直接放大成通用生产方案。


知识点:权限错误为什么容易制造“空备份”

如果把容器外目录或命名卷挂到 /backup,容器中的 mongodump 进程可能没有写权限。最危险的处理方式是:

shell
mongodump --quiet ... || true

它同时隐藏错误输出和退出状态,后续流程可能把空文件上传成“成功备份”。更可靠的脚本应满足:

  • 使用 set -euo pipefail 或等价的错误处理。
  • 在备份前明确检查目标目录权限。
  • 不吞掉 mongodump 的退出码。
  • 检查归档非空,再计算摘要和上传。
  • 上传后从远端重新下载一份做周期性恢复演练。

工具安静并不等于任务成功。备份流程应该主动失败,并把失败暴露给监控。


实操:清理恢复演练数据

确认验收记录已经保存后,删除恢复数据库和容器内临时文件:

shell
docker exec paperboat-mongo mongosh --quiet --eval '
printjson(db.getSiblingDB("bookstore_restore").dropDatabase())
'
javascript
{ ok: 1, dropped: 'bookstore_restore' }
shell
docker exec paperboat-mongo rm -f \
  /tmp/bookstore.archive \
  /tmp/bookstore-restore.archive

如果这些归档只为练习,继续删除 backups 中刚生成的文件;真实备份则应转移到受控、加密并有保留策略的存储,而不是长期留在项目目录。

结果展示:恢复库已经消失

shell
docker exec paperboat-mongo mongosh --quiet --eval '
print(db.getMongo().getDBNames().includes("bookstore_restore"))
'
text
false

false 表示恢复演练没有留下额外数据库,原始 bookstore 仍然存在。

这一节的完成标准不是“生成了一个文件”,而是归档非空、摘要可校验、15 个文档恢复成功、三个集合数量吻合,并确认关键索引与 validator 一起恢复。


小测

1
为什么副本集不能替代备份?
2
一次可靠的恢复验收应检查哪些内容?
上一章从单文档原子性到事务下单下一章认证、授权与安全边界