入门指南

备份任务返回 0 之后:三个工具各验了什么,一份恢复演练验收单

备份成功只证明命令跑完了。把 pg_dump、SQLite backup 与 restic check 各自验过和没验的边界拆开,再给一份可照抄的恢复演练验收单。

备份跑完只证明命令成功;能不能恢复要单独验。

作者
AOPC 编辑部
发布
AOPC 编辑部
发布时间
更新
2026/10/27
核查
已复核
一个人在终端前把备份作业、校验结果与恢复演练列成三列,逐项打勾
经营者责任与 AI 执行层的关系

先看答案

适合谁
  • 自己维护数据库、没有专职运维的一人公司
  • 把 pg_dump 或复制数据库文件当成日常备份的人
  • 用 restic 但只看 check 有没有报错的人
你会获得
先分清哪一层在证明什么,再决定多久验一次。
发布时间
实质更新
发布
AOPC 编辑部

60 秒结论

一分钟看懂这篇文章

备份跑完只证明命令成功;能不能恢复要单独验。

人负责

  • 决定哪些数据必须能恢复
  • 安排演练窗口与隔离环境

AI 负责

  • 核对三类工具的校验边界
  • 生成恢复脚本与验收记录
  1. 列数据
  2. 定标准
  3. 跑演练
  4. 记结果

直接答案

「备份成功」这句话里混着三件不同的事,而它们由三套不同的机制证明:

层 你实际在做什么 能证明什么 不能证明什么
① 备份作业 跑 pg_dump、备份 API 或 restic backup 命令按预期结束、产物已写出 产物能读、能还原、数据没坏
② 校验 restic check、SQLite integrity_check、检查 dump 能否被工具打开 结构、索引、校验和层面没有已知错误 数据与业务一致、能恢复
③ 恢复演练 把产物恢复到一个干净环境并按标准验收 这份产物在这台机器、这个版本、这套步骤下能还原 下一次也能——除非你重复做

最容易被跳过的是第三条:它需要一台空机器、一段不被打断的时间和一张验收标准,而前两条恰好都能以退出码 0 结束。于是「每天都有备份」和「出事时能恢复」之间,常常只有一条从未被验证过的假设。

PostgreSQL 文档把备份方式分成三类:SQL dump、文件系统级备份、持续归档(continuous archiving),并写明三者各有强弱。本文只处理一人公司最常见的前两类里的三个具体工具——pg_dump、SQLite 的备份 API、restic check——以及一个它们都替代不了的动作:恢复演练。托管数据库的自动快照不在本文范围内:那是厂商实现,你能验的只有「恢复出来对不对」。

一、pg_dump:一致导出,但不是「生产库的备份方案」

官方文档先给了能力,紧接着给了边界,两句话要连起来读:

pg_dump is a utility for exporting a PostgreSQL database. It makes consistent exports even if the database is being used concurrently.

Note, however, that except in simple cases, pg_dump is generally not the right choice for taking regular backups of production databases.

这不是「pg_dump 不好用」,而是「常规生产库备份」这个用途不合适(简单场景除外)。同一章解释了它的产物是什么:dump 是逻辑快照——「the dump represents a snapshot of the database at the time pg_dump began running」,恢复时要重新执行 SQL 或交给 pg_restore 重建对象,而不是把数据文件搬回去。

边界一:它只导一个数据库

pg_dump only dumps a single database. To export an entire cluster, or to export global objects that are common to all databases in a cluster (such as roles and tablespaces), use pg_dumpall.

pg_dumpall 页把「缺什么」写得更具体:它还会导出所有数据库共用的全局对象,即 database roles、tablespaces,以及配置参数的权限授予,并明确注明 pg_dump 不保存这些对象。而恢复这份脚本时,「you will need superuser privileges to execute the saved script in order to be allowed to add roles and create databases」。

这解释了一类很典型的翻车:业务表一行不少地恢复了,应用却连不上——因为角色、权限或表空间从来没进过那份 dump。

边界二:格式决定你能怎么恢复

格式 产物 恢复方式 关键差异
默认(plain) 纯文本 SQL psql 数据库不会自动创建,要先 createdb -T template0
-Fc(custom) 压缩归档 pg_restore 可挑选对象、可重排;默认压缩
-Fd(directory) 每表一个文件的目录 pg_restore 唯一支持 -j 并行 dump 的格式

文档对 custom/directory 的评价是「most flexible output format in that it allows manual selection and reordering of archived items during restore」。而 -j njobs 「can only use this option with the directory output format」——想靠并行缩短备份窗口,就只有这一条路。

边界三:版本方向是单向的

Because pg_dump is used to transfer data to newer versions of PostgreSQL, the output of pg_dump can be expected to load into PostgreSQL server versions newer than pg_dump’s version.

反方向没有保证:不能从比自己主版本更新的服务器上 dump(工具会直接拒绝),也不保证能把 dump 载入更旧的主版本。跨版本恢复时,文档建议用 --quote-all-identifiers,避免不同版本保留字差异带来的问题。

一人公司的落地结论(编辑判断):如果日常备份就是一条 pg_dump,验收单上至少要有三行——只备了业务库还是连角色一起备了;恢复目标是空实例还是覆盖现有库;pg_dump 与目标服务器的版本方向对不对。三行里任何一行答不上来,那份 dump 就只能算「有文件」,不能算「能恢复」。

二、SQLite:为什么「先加共享锁再 cp」会出三种问题

SQLite 的文档把「用文件复制做备份」的代价逐条列了出来,原文给了三个缺点:

Any database clients wishing to write to the database file while a backup is being created must wait until the shared lock is relinquished.

It cannot be used to copy data to or from in-memory databases.

If a power failure or operating system failure occurs while copying the database file the backup database may be corrupted following system recovery.

三条分别对应:写入被阻塞、内存库复制不了、复制中途掉电可能得到一个损坏的副本。注意第三条最阴——cp 成功返回、文件大小也对,但副本可能已经不可用,而你要到恢复那天才会发现。

文档给出的替代方案有三个,各自解决不同问题:

方案 原文口径 适用
Online Backup API 把一个库的内容复制进另一个库文件,「replacing any original contents of the target database」;可增量,「the source database does not need to be locked for the duration of the copy, only for the brief periods of time when it is actually being read from」 应用内定时备份、需要控制锁时间
VACUUM INTO 「will make a vacuumed copy of a live SQLite database into a separate file」 一条命令拿到整理过的副本
sqlite3_rsync 「makes a copy of a live SQLite database to or from a remote system using an SSH connection」 直接把副本送到另一台机器

校验这一层还有一条命令:PRAGMA integrity_check 「does a low-level formatting and consistency check of the database」,检查范围包括记录顺序、格式错误的记录、缺失页、缺失或多余的索引项、约束错误、freelist 完整性,以及被重复使用或完全未使用的区段。

但它是结构检查:能发现文件层面的损坏,不能证明「这张表里的数字和你业务上以为的一致」。这一点和下一节的 restic 默认检查是同一类边界。

一人公司结论(编辑判断):如果备份脚本里写的是 cp app.db backup/,第一件事不是加频率,而是换成语义正确的复制方式;第二件事是给副本跑一次 PRAGMA integrity_check,把输出留档。两件事都做完,才轮得到「多久备一次」。

三、restic:默认的 no errors were found 只证明结构

restic 把校验分成两层,原文写得很清楚:

Structural consistency and integrity, e.g. snapshots, trees and pack files (default)

Integrity of the actual data that you backed up (enabled with flags, see below)

默认的 check 不读数据本身:

By default, the check command does not verify that the actual pack files on disk in the repository are unmodified, because doing so requires reading a copy of every pack file in the repository.

要验数据,得显式加 --read-data。而它有明确成本:

Since –read-data has to download all pack files in the repository, beware that it might incur higher bandwidth costs than usual and also that it takes more time than the default check.

一个折中是 --read-data-subset=n/t:把仓库的 pack 文件分成 t 组,每次只验第 n 组,分 t 次覆盖全仓;也可以按百分比或大小随机抽样。

还有一条最容易被忽略的因果:

If check reports an error in the repository, then you must repair the repository. As long as a repository is damaged, restoring some files or directories will fail. New snapshots are not guaranteed to be restorable either.

也就是说,仓库损坏之后,继续往里写新快照并不能保证新快照可恢复。这时候「备份作业还在成功」反而最危险:你以为在积累备份,实际在积累不可恢复的快照。

一人公司结论(编辑判断):把 restic 的校验分成三档,并把「上次全量校验日期」写进日历——

  • 每次备份后:默认 check(便宜,查结构);
  • 每周或每月:check --read-data-subset=n/t,按仓库大小和带宽决定 t;
  • 每季度:check --read-data 全量,或分几次把全仓覆盖一遍。

只做第一档,等于永远没验过「数据还在不在」。

四、恢复演练:一份可以照抄的验收单

下面这张表是编辑判断,不是任何一家厂商的认证标准;但每一行的通过标准都能被第三方复核。

# 检查项 通过标准 留下什么记录
1 隔离环境 恢复到独立实例、独立目录或临时库,全程不连生产 主机名、库名、开始与结束时间
2 工具与版本方向 pg_restore/psql 版本不低于 dump 来源主版本;SQLite 与 restic 客户端能正常打开该产物/仓库 两侧版本号
3 对象齐全 表、索引、视图、触发器、扩展的数量与生产一致;PostgreSQL 另核角色与 tablespace 两侧计数对比
4 行数与关键聚合 每张核心表 count(*)、最大时间戳、一两个金额合计与生产一致,差异为 0 或已解释 查询语句与结果
5 恢复日志 恢复过程错误条数为 0;有错误就必须逐条解释,不能「忽略后继续」 原始日志
6 业务读写 应用连到恢复库,完成一次真实读取和一次写入(写到临时记录),再回滚 请求、响应、回滚结果
7 耗时 从开始恢复到「可对外服务」的分钟数,作为下一次的基线 分钟数(本文不设目标值)
8 清理 演练库或目录删除或隔离,临时凭据回收 删除记录

三个容易做错的细节:

① 抽最老的一份,不只验最新的一份。 最新快照通常最健康,而你真正需要备份的那天,往往是「三周前那份还没被新备份覆盖的」。保留策略里最老的那份如果打不开,等于保留期是假的。(编辑判断)

② PostgreSQL 的演练要包含「建库」这一步。 文本 dump 恢复前不会自动建库,得先 createdb -T template0;如果这一步在演练里被跳过,真实恢复时就会卡在一个和备份无关的地方。

③ 仓库密码要按「灾难时拿得到」的标准准备。 restic 仓库需要密码才能打开(文档示例里每一条命令都在 enter password for repository:)。演练时请用灾难当天你真正能拿到的那份凭据去开,而不是只存在这台机器钥匙串里的那一份。(编辑判断)

五、五个常见误读

  1. 「作业退出码是 0,所以备份可用。」 退出码只覆盖第 ① 层。文件写出成功与文件可恢复是两件事。
  2. 「restic check 没报错,数据就是完整的。」 默认只查结构;不读 pack 文件。要覆盖数据,得 --read-data 或分批 --read-data-subset。
  3. 「pg_dump 就是全库备份。」 它只导一个数据库,角色、tablespace 等全局对象要 pg_dumpall,而且恢复脚本需要超级用户权限。
  4. 「有备份就等于有恢复。」 备份和恢复之间隔着:产物是否可读、版本方向对不对、凭据拿不拿得到、有没有在干净环境里跑过一次。
  5. 「备份越频繁越安全。」 频率只增加副本数量,不增加可恢复性;一份从未验证过的高频备份,只是更多份未验证的文件。(编辑判断)

六、一周、一月、一季

节奏 动作 完成标志
每次备份后 看作业结果;restic 跑默认 check;SQLite 跑 integrity_check 结果与产物路径都进了同一份日志
每周 抽一份最近的产物,恢复到临时目录,只验「能打开、表数对」 一条可重复执行的命令
每月 restic check --read-data-subset=n/t;PostgreSQL 核一次角色与 tablespace 清单 覆盖到全仓的一个分组
每季 走完整张验收单,包含业务读写与耗时记录 一份带日期、执行人、结果的记录
变更时 换机器、升主版本、换存储后端之后重跑一次 新环境下仍能恢复

如果只能记住一件事:把「最近一次成功恢复的时间」当成备份系统的真正指标,而不是「最近一次备份成功的时间」。前者需要你做过,后者只需要 cron 跑过。


边界说明:本文不写 RPO/RTO 数字,不写「几分钟内恢复」,也不比较三个工具谁更好——它们解决的问题不同。文中的频率建议、验收单与「抽最老一份」等做法均标注为编辑判断;所有关于工具行为的陈述都来自上列官方文档原文。托管数据库的自动快照、云厂商的跨区复制不在本文取证范围内。

来源

以下页面均于 2026-10-27 逐条访问并验证为 HTTP 200;字节数为当日抓取的实际大小。

  1. pg_dump — PostgreSQL 18核查于 2026/10/27(200,89 919 B):一致导出且不阻塞读写;「except in simple cases, pg_dump is generally not the right choice for taking regular backups of production databases」;只导单个数据库、全局对象交给 pg_dumpall;plain/custom/directory 三种格式与 pg_restore 的选择性恢复;-j 仅支持 directory 格式;版本方向的单向兼容与 --quote-all-identifiers。
  2. pg_dumpall — PostgreSQL 18核查于 2026/10/27(200,47 724 B):把一个集群的所有数据库写进一个脚本;额外导出 database roles、tablespaces 与配置参数的权限授予;注明 pg_dump 不保存这些对象;执行脚本需要超级用户权限才能创建角色与数据库。
  3. Backup and Restore — PostgreSQL 18核查于 2026/10/27(200,17 324 B):备份分为 SQL dump、文件系统级备份、持续归档三类,各有强弱。
  4. SQL Dump — PostgreSQL 18核查于 2026/10/27(200,29 843 B):dump 是 pg_dump 启动时刻的内部一致快照;不阻塞其它操作(需排它锁的操作除外);文本 dump 用 psql 恢复且数据库不会自动创建,需先从 template0 建库。
  5. SQLite Backup API核查于 2026/10/27(200,28 195 B):共享锁加文件复制的三个缺点(阻塞写入、不支持内存库、掉电可能损坏副本);Online Backup API 的替换语义、增量复制与短暂加锁;VACUUM INTO 与 sqlite3_rsync 两种替代。
  6. PRAGMA Statements — SQLite核查于 2026/10/27(200,139 936 B):PRAGMA integrity_check 做低层格式与一致性检查,列出其检查项(记录顺序、格式错误、缺失页、索引项缺失或多余、约束错误、freelist 完整性、重复或未使用区段)。
  7. Working with repositories — restic核查于 2026/10/27(200,61 215 B):结构一致性与数据完整性两层检查;默认不读 pack 文件;--read-data 需下载全部 pack、带宽与时间成本更高;--read-data-subset=n/t 分批覆盖;仓库损坏后部分恢复会失败且新快照也不保证可恢复,须先修复。

唯一主行动

把下一步变成可执行动作

把备份与回滚写进产品维护流程