某个笔记系统每天凌晨生成数据库导出和附件压缩包。一个月后服务器磁盘损坏,导入数据库成功,打开笔记却发现图片缺失。备份任务从未报错,但它保存的不是一个完整、可使用的业务状态。
要解决这种问题,需要同时回答三个问题:备份包含什么时刻的数据,恢复需要哪些外部条件,以及恢复完成后怎样证明业务正确。下面以 PostgreSQL 与附件目录为假设模型,命令均用于独立演练环境,没有对生产数据执行恢复。
文件完整与业务一致不是同一件事
数据库里有一条附件记录,并不保证对应文件已经进入同一次归档。假设 02:00 开始导出数据库,02:02 用户上传新附件,02:05 才打包文件:归档可能包含数据库中没有引用的文件。反过来,如果文件先被删除、数据库稍后提交删除事务,备份还可能出现记录指向不存在文件。
PostgreSQL 的 pg_dump 能在数据库并发使用时取得一致的数据库导出,但范围是一套数据库对象;角色等全局对象需要另外处理。这个一致性不自动延伸到应用的附件目录。pg_dump 官方说明
因此备份单元应由业务定义。对静态博客,它可能是文章、主题、图片、依赖锁文件和部署说明;对有附件的系统,还需要明确数据库记录与文件的提交顺序。系统越简单,越应该把这个边界写清楚,避免日后把“有一份网页”误认为“能继续编辑和发布”。
三种一致性方案各有代价
小型个人系统可以在计划维护窗口暂停写入,等待在途写请求结束后,再导出数据库并归档附件。它容易理解,但需要真实阻止所有写入来源,包括后台任务,不能只把网页按钮置灰。
如果附件采用不可变对象:上传新文件永不覆盖旧键、删除先做逻辑标记且延迟物理回收,那么可以先确定数据库恢复点,再保留覆盖该恢复点的附件集合。这样能够减少停写,但要付出额外存储与垃圾回收复杂度。旧文件保留多久,必须大于备份窗口与允许回退的历史范围。
存储快照也不等同于应用事务快照。对于运行中的 PostgreSQL,直接随意复制数据目录缺乏保证;文件级备份需要数据库停止,或使用满足文档条件的一致快照等办法。PostgreSQL 文件系统级备份
可以先估算可接受停写时间,再决定方案复杂度。如果每晚十秒停写能接受,先建立清晰、可恢复的流程;确实需要连续写入时,再投资不可变附件、日志归档和自动化校验。先选择复杂机制、却从未做过恢复,其可靠性往往不如简单流程。
把 RPO 与 RTO 写成可测量承诺
RPO 关注允许损失多少近期数据;RTO 关注多久恢复到可用状态。这里把“恢复点”定义为备份代表的数据时间,而不是压缩包上传成功的时间。
假设每天 02:00 取得数据快照,02:20 上传完成,18:00 故障。若只能用这份备份,已知数据缺口约为 16 小时;不能因为上传在 02:20 完成,就宣称只损失 15 小时 40 分。若当天备份失败,只剩前一天,则风险会进一步增大。监控必须同时记录最新成功归档时间与最新可恢复数据时间。
再假设恢复要下载 12 分钟、解密和校验 3 分钟、导入 18 分钟、检查附件 7 分钟、切换入口与验收 5 分钟,串行估算 RTO 是 45 分钟。实际还要计入发现故障、获取凭据、准备实例与人工决策的耗时。一次在已准备好机器上的快速导入测试,只覆盖 RTO 的一部分。
备份产物应该能独立说明自己
每个备份目录至少应记录:应用版本、数据库主版本、备份工具版本、开始与完成时间、采用的一致性方式、附件根目录以及恢复需要的扩展。敏感配置与加密密钥应单独受控保存,不能与唯一一份密文只放在同一故障域。
以下 Linux 示例只创建新的暂存目录,读取演练系统数据。执行前应已完成该演练系统的停写和在途任务排空;notes-readonly 是预先配置的 PostgreSQL 连接服务名,权限需要足以读取备份对象。目录名和账号均是占位示例。
1 | set -eu |
不要把正在运行的数据库数据目录塞进上面的 tar。这里归档的是业务附件。命令成功后还应补齐版本清单、加密传输到独立存储,并确认上传对象可重新读取。哈希能发现传输或存储改变,不能证明数据库内容和附件引用正确。
示例使用 custom 格式是为了便于检查归档目录和选择恢复方式,不等于它适合所有规模。大型数据集的恢复时间可能主要由索引建立、磁盘吞吐或数据库参数决定,应通过恢复实测评估。
恢复演练只在独立目标进行
首先准备一个与生产网络隔离的测试数据库实例,禁止它发送真实邮件、调用支付或访问生产对象存储。确认 notes-drill 连接服务指向该实例,再执行下列示例。新的数据库名用于避免覆盖已有数据库,失败时保留现场,不在脚本末尾自动清理。
1 | set -eu |
--single-transaction 使恢复整体成功或回滚,并隐含遇错退出;它不能与并行恢复任务同时使用,对非常大的对象集合也有资源取舍。--no-owner 和 --no-privileges 简化隔离演练,正式恢复则需另行恢复正确的角色与授权。pg_restore 官方说明
附件先列出归档内容,再解到新建暂存目录;对来源不可信的归档,不应直接解压到系统目录。恢复应用时使用测试配置,不能因为数据库名不同就假设所有外部依赖已经隔离。
“能打开首页”远远不够
验收应从业务不变量开始:每条有效附件记录都有可读文件;文件摘要与记录一致;笔记数量、关键时间范围和抽样内容符合备份清单;测试账号能够登录、读取旧笔记并创建新笔记。恢复后只能读、不能写,常常意味着序列、权限或应用配置仍有遗漏。
数据量很大时,可以先做全量引用存在性检查,再对内容做分层抽样:最新数据、历史数据、大文件和特殊字符文件名分别覆盖。抽样不能证明百分之百正确,但比只选一个最简单页面更能暴露结构性问题。检查脚本也应与业务版本共同维护,否则字段变化后可能悄悄失效。
快速回退与灾难恢复分开建设
同机保留旧版本适合修复错误发布,却不能抵抗整块磁盘损坏。异机备份可以抵抗单机故障,但可能仍会被同一账号误删。按自身威胁模型选择独立权限、版本保留或离线副本,目的是避免一次操作同时破坏所有恢复路径。
需要更小 RPO 时,可评估 PostgreSQL 基础备份结合 WAL 归档的时间点恢复;它依赖基础备份和所需日志连续可用,并非保留几个导出文件就能任意回到某秒。PostgreSQL 连续归档与时间点恢复
最终应留下一份可复演的记录:备份恢复点、实际数据损失、各阶段耗时、失败步骤和修复措施。备份任务的绿色状态只是开始,能够在独立环境恢复并通过业务验收,才说明它真正承担了恢复责任。