跳至内容

2.6 最小逻辑备份闭环

生成一个 dump 文件只是开始。最小闭环必须回答:文件里有什么、由哪个版本生成、能否在隔离目标恢复、恢复后的状态是否符合预期、哪些生产恢复目标仍未覆盖。

2.6.1 pg_dump 的对象、模式与自定义格式

pg_dump 连接一个数据库,从一致快照读取逻辑对象定义与数据。它不会阻塞普通读写,但会持有访问共享锁来防止被导出的表在过程中遭到破坏性 DDL;长时间快照也可能影响 vacuum 回收。生产上不能因为“在线 dump”就忽略运行窗口和监控。

先验证夹具,再创建目录:

mkdir -p evidence/ch02/backup
psql -X -w "service=pg36-admin" \
  -v ON_ERROR_STOP=1 \
  -f verify.sql \
  >evidence/ch02/backup/source-verify.txt \
  2>evidence/ch02/backup/source-verify.stderr

使用自定义格式:

pg_dump -w \
  --format=custom \
  --verbose \
  --file=evidence/ch02/backup/pg36_shop.dump \
  "service=pg36-admin application_name=pg36-ch02-dump" \
  >evidence/ch02/backup/dump.stdout \
  2>evidence/ch02/backup/dump.stderr

-w 禁止密码提示;凭据来自 passfile。不要把 stdout 和 stderr 合并,因为 pg_dump --verbose 的进度与警告写入 stderr,警告必须进入验收。

为什么选择 custom

格式恢复工具选择对象并行能力本章用途
plainpsql生成后可读但难安全重排审查简单 SQL
custom (-Fc)pg_restore可列清单、过滤、重排恢复可并行本章默认
directory (-Fd)pg_restore可列清单、过滤、重排dump 与 restore 可并行大库与并行任务
tar (-Ft)pg_restore可选择不支持并行 dump兼容特定归档流程

custom 默认压缩且是单文件,适合小型教学闭环。真正的大库可能选择 directory format 与并行 worker,但并行数必须结合服务器 CPU、存储、网络和锁评估。

dump 的边界

普通 pg_dump 只处理一个数据库。它不包含跨数据库共享的角色与表空间定义;这类全局对象可由:

pg_dumpall -w --globals-only \
  --database="service=pg36-admin dbname=postgres" \
  >evidence/ch02/backup/globals.sql

单独导出。globals 文件可能含有角色口令哈希与敏感 ACL,应按秘密材料保护。本章恢复演练使用 --no-owner --no-privileges,故不依赖它;这也意味着恢复结果有意不保留原 owner/ACL,不能冒充完整灾备演练。

pg_dump -t--schema 只选择匹配对象,不会自动保证所有依赖都包含。一个“成功生成”的局部 dump 可能无法恢复到空数据库。除非任务明确处理依赖,本章先 dump 整个 pg36_shop

客户端版本也是输入

先记录:

pg_dump --version
psql -X -w "service=pg36-admin" -Atc \
  "SELECT current_setting('server_version')"

pg_dump 不能导出主要版本高于自己的服务器;较新的 pg_dump 可以读取较老服务器,但输出通常面向较新工具链。逻辑 dump 常用于升级,却没有“向旧版本降级必然成功”的承诺。扩展、排序规则和 SQL 语义仍需单独验证。

最后为归档文件计算客户端哈希:

sha256sum evidence/ch02/backup/pg36_shop.dump \
  >evidence/ch02/backup/pg36_shop.dump.sha256

哈希证明文件字节未变化,不证明内容完整、可信或可恢复。

2.6.2 pg_restore 的清单、选择性恢复与验证

第一步不是恢复,而是检查:

pg_restore \
  --list \
  evidence/ch02/backup/pg36_shop.dump \
  >evidence/ch02/backup/pg36_shop.list

清单列出 pre-data、data、post-data 阶段的模式、表、数据、约束、索引和 ACL 等条目。可以复制清单,按行前加分号排除对象,再用 --use-list 恢复;但手工删除依赖条目可能得到不完整数据库。

还可以把归档展开为 SQL 供审查:

pg_restore \
  --file=evidence/ch02/backup/preview.sql \
  evidence/ch02/backup/pg36_shop.dump

这一步非常重要:恢复来自不可信服务器的 dump,会在目标执行源端超级用户能够植入的任意代码。局部过滤不会消除这一风险。未知来源归档必须先审查,并在严格隔离与最小权限环境处理。

恢复到隔离数据库

不要对源数据库使用 --clean 试验恢复。创建一个名称固定、用途明确的临时数据库:

psql -X -w \
  "service=pg36-admin dbname=postgres" \
  -v ON_ERROR_STOP=1 <<'PSQL'
SELECT NOT EXISTS (
  SELECT 1
  FROM pg_catalog.pg_database
  WHERE datname = 'pg36_restore'
) AS restore_name_available
\gset

\if :restore_name_available
  CREATE DATABASE pg36_restore TEMPLATE template0;
\else
  \warn '[restore] refused: database pg36_restore already exists'
  DO $restore_error$
  BEGIN
      RAISE EXCEPTION 'restore target name is already in use';
  END
  $restore_error$;
\endif
PSQL

本章要求从干净 template0 新建。若同名数据库已经存在,上面的保护会返回非零;先确认它是否属于早先演练,再选择单独清理或更换名称,绝不自动覆盖。

恢复:

pg_restore -w \
  --exit-on-error \
  --single-transaction \
  --no-owner \
  --no-privileges \
  --dbname="service=pg36-admin dbname=pg36_restore application_name=pg36-ch02-restore" \
  evidence/ch02/backup/pg36_shop.dump \
  >evidence/ch02/backup/restore.stdout \
  2>evidence/ch02/backup/restore.stderr
  • --exit-on-error 避免默认“继续恢复、最后报告错误数量”的行为;
  • --single-transaction 保证本次小型恢复要么全部提交、要么全部回滚,并隐含 exit-on-error;
  • --no-owner --no-privileges 让实验不依赖源角色,把对象归当前恢复角色所有;
  • 大型归档可能因锁数量、事务长度而不适合单事务,生产方案必须实测。

--jobs 能并行装载数据和创建部分对象,但不能与 --single-transaction 同用。并行恢复的成功条件仍是状态验证,不是“worker 都退出了”。

用语义摘要验证

分别在源与恢复库执行:

SELECT
    count(*) AS row_count,
    min(fixture_id) AS min_id,
    max(fixture_id) AS max_id,
    md5(
      string_agg(
        fixture_id || '|' || sku || '|' || payload,
        E'\n'
        ORDER BY fixture_id
      )
    ) AS checksum
FROM shop.ch02_fixture;

期望两边均为 100110000ed4599a6ed75e4441f5211909480fa。再验证列、约束和索引,而不是只查行数:

\d+ shop.ch02_fixture

因为本次使用 --no-owner --no-privileges,owner 与 ACL 应与源库不同;这不是失败,而是任务选择的恢复语义。验证报告必须明确哪些属性要求相同、哪些有意重映射。

完成后,删除 pg36_restoreR2·破坏性演练。必须先确认它只属于本实验、终止范围仅限这个数据库,再携带精确令牌执行:

psql -X -w \
  "service=pg36-admin dbname=postgres" \
  -v ON_ERROR_STOP=1 \
  -v confirm_drop=DROP_PG36_RESTORE <<'PSQL'
SELECT :'confirm_drop' = 'DROP_PG36_RESTORE' AS drop_confirmed
\gset

\if :drop_confirmed
  SELECT pg_terminate_backend(pid)
  FROM pg_stat_activity
  WHERE datname = 'pg36_restore'
    AND pid <> pg_backend_pid();
  DROP DATABASE pg36_restore;
\else
  DO $drop_error$
  BEGIN
      RAISE EXCEPTION 'drop confirmation is required';
  END
  $drop_error$;
\endif
PSQL

不要把清理动作附在默认备份命令后;保留恢复目标供人工验收,确认后再独立清理。

2.6.3 与 ch21《未雨绸缪:备份体系与恢复演练》的边界

本节证明的是“逻辑对象可以导出、检查、恢复和验证”,不是“生产数据已经安全”。两者之间至少还差:

生产问题本节是否覆盖ch21 要补什么
整个实例的物理恢复基础备份、WAL 归档、pgBackRest
任意时间点恢复时间线、恢复目标、PITR 演练
角色、表空间与配置部分全局对象、参数、扩展包和基础设施清单
RPO / RTO业务目标、备份频率、恢复计时与容量
保留、异地和不可变副本仓库、生命周期、加密、访问控制
自动验证与告警仅手工样例周期性恢复演练、失败告警、证据归档
大库恢复性能并行度、网络、磁盘、锁与资源预算
高可用拓扑重建Patroni、复制槽、服务与成员恢复

Pigsty 的生产备份参考实现以 pgBackRest 物理备份与 WAL 归档为核心;逻辑 dump 更适合对象级迁移、审查和辅助恢复。两者不是互相替代的单选题。

一份文件不是备份结论

至少经历以下状态,才能把本节称为一次演练:

    flowchart LR
  A["源状态已验证"] --> B["dump 成功"]
  B --> C["归档哈希已记录"]
  C --> D["清单与 SQL 已检查"]
  D --> E["隔离目标恢复成功"]
  E --> F["数据与结构验证通过"]
  F --> G["耗时、错误和差异已记录"]
  

链路中任一步失败都应保留证据,而不是删除文件重新跑到“看起来成功”为止。

本节验收

  • dump 客户端与服务端版本都进入清单;
  • custom 归档有 SHA-256,并能由 pg_restore --list 读取;
  • 恢复前查看了清单或展开 SQL;
  • 恢复目标与源数据库隔离;
  • pg_restore 遇错即停,恢复后验证结构、行数和校验和;
  • 报告明确 owner/ACL 是否保留;
  • 能列出本节没有覆盖的 RPO、RTO、WAL、保留和异地问题。

参考资料


上一节:最小 pgbench 工作负载 · 返回本章目录 · 下一节:实战:把人工操作变成可重跑任务 · 查看全书目录 · 查看索引中心

最后更新于