数据库专题25:备份与恢复演练——没有做过恢复,就不算有备份
备份文件存在磁盘上,只能说明“复制成功”,不能说明系统能恢复。真正的目标是给博客项目定义 RPO(最多丢多少数据)和 RTO(多久恢复服务),然后分别对 PostgreSQL、MongoDB、Redis 做可重复的导出、破坏、恢复和校验。本篇所有命令都可在projects/database-platform的 Docker Compose 中练习。
上一篇练习讲解
上一篇的安全练习应创建blog_report只读账号、Redis ACL,并用注入字符串测试搜索。若测试失败,优先检查应用是否绕过 ORM 使用了字符串拼接;权限错误应在数据库端出现,而不是只依赖接口代码。备份也要使用专用只读账号,不能把管理员密码写入脚本。
1. 明确 RPO/RTO 和备份对象
本项目设定:文章和用户的 RPO 为 15 分钟、RTO 为 30 分钟;Redis 排行榜是可重建缓存,RPO 可为 24 小时。PostgreSQL 要做逻辑备份加 WAL/PITR,MongoDB 保存文章版本和审计,需保留时间点;Redis 只备份持久计数,普通缓存不必恢复。
PostgreSQL 业务事实 ── pg_dump + WAL ──> 对象存储 MongoDB 版本/审计 ─── mongodump ──> 加密归档 Redis 持久计数 ────── BGSAVE/导出 ──> 可选 ↓ 每月进行一次“破坏后恢复”演练2. PostgreSQL 逻辑备份
先在容器内生成自定义格式备份。密码通过环境变量传递,避免出现在命令历史:
$env:PGPASSWORD ="blog"docker compose exec-T postgres pg_dump-U blog-d blog-Fc>.\backup\blog-$(Get-Date-Format yyyyMMdd-HHmm).dump自定义格式支持并行恢复和选择性恢复;如果只需要 schema,可使用--schema-only。备份完成后检查文件大小和目录清单:
pg_restore-l.\backup\blog-latest.dump|Select-Object-First 12 pg_restore--list.\backup\blog-latest.dump|Select-String"TABLE DATA"恢复到临时数据库,不要直接覆盖生产库:
docker compose exec-T postgres createdb-U blog blog_restoreGet-Content.\backup\blog-latest.dump-AsByteStream|docker compose exec-T postgres pg_restore-U blog-d blog_restore--clean--if-exists docker compose exec-T postgres psql-U blog-d blog_restore-c"select count(*) from articles;"验证行数、关键用户、文章版本和约束。恢复成功后再切换连接串或进行逻辑复制,不能只看到pg_restore返回 0 就宣布完成。
3. MongoDB 文章版本与审计备份
New-Item-ItemType Directory-Force.\backup\mongo|Out-Nulldocker compose exec-T mongo mongodump--db blog--archive--gzip >.\backup\mongo\blog.archive.gz恢复到隔离数据库进行比对:
Get-Content.\backup\mongo\blog.archive.gz-AsByteStream|docker compose exec-T mongo mongorestore--archive--gzip--nsFrom='blog.*'--nsTo='blog_restore.*'--drop docker compose exec-T mongo mongosh--quiet--eval"db.getSiblingDB('blog_restore').article_versions.countDocuments({})"生产副本集可使用--oplog保证导出期间的一致时间点;大集合恢复时注意磁盘和索引重建时间。审计集合涉及合规,备份文件必须加密并限制读取角色。
4. Redis 备份的边界
Redis 的缓存 key 可以在 PostgreSQL/Mongo 恢复后重建。若排行榜计数还没落库,使用 AOF 或 RDB 作为短期补救:
docker compose exec redis redis-cliBGSAVE docker compose exec redis redis-cliLASTSAVE docker compose exec redis redis-cli--user blog_app--pass change-me-redis GET blog:article:42:views不要把dump.rdb当作跨版本长期备份;恢复 Redis 后先让应用以只读降级启动,确认 key 命名空间和 TTL,再开放写流量。敏感 session 应设置短 TTL,恢复旧 session 可能带来账号安全问题。
5. 破坏与恢复实验
在测试环境插入标记数据:
insertintoarticles(title,body,author_id,status)values('restore-check','backup marker',1,'published');记录当前时间和三类数据库行数,然后删除restore-check,再从备份恢复到临时库。验收脚本示例:
importpsycopgdefassert_restore(dsn:str)->None:withpsycopg.connect(dsn)asconn:row=conn.execute("select title, body from articles where title=%s",("restore-check",)).fetchone()assertrow==("restore-check","backup marker")如果恢复后缺少该行,说明备份时间晚于插入或备份文件路径错误。若行存在但外键失败,检查恢复顺序和--disable-triggers的使用范围;不要为了让命令成功而永久禁用约束。
6. 自动化与保留策略
备份脚本应输出成功/失败、耗时、文件大小、校验和,并在失败时告警。保留策略可以是每日 7 份、每周 4 份、每月 12 份;删除旧备份前确认最新一份已经通过恢复抽检。对象存储启用版本控制和服务端加密,下载恢复时使用临时凭证。
验收记录
pg_dump blog-latest.dump -> 文件可由 pg_restore -l 读取 blog_restore articles 行数 = 原库行数,restore-check 存在 mongorestore blog_restore.article_versions = 原集合数量 Redis BGSAVE 返回 Background saving started 恢复演练从停止写入到校验通过耗时 6 分钟,满足 RTO 30 分钟课后练习
写backup.ps1:创建带日期的目录,导出 PostgreSQL/MongoDB,计算 SHA-256 并输出 JSON 报告;写restore-check.ps1自动创建临时库并验证标记数据。报告中记录 RPO/RTO,下一篇使用这些数据做慢查询和压测基线。