21.1 从恢复场景设计备份
备份设计最容易犯的错误,是从一个漂亮的日历开始:
周日全备
周一到周六增量
保留 14 天它回答了“工具何时运行”,却没有回答:
什么会丢?
丢了什么算业务损失?
要回到哪个边界?
由谁判断边界正确?
依赖也丢失时怎么办?
多快恢复到什么能力?正确顺序是从场景与真相出发,再选择恢复机制、保留和调度。
21.1.1 误删、介质故障、区域故障与合规留存
先定义“损失”
数据库恢复不是把字节放回磁盘,而是重新建立被业务接受的事实。至少区分:
logical loss
数据库仍运行,但某些正确事实被删除、覆盖或错误变更
physical loss
数据文件、WAL、设备或整个集群不可用/不可信
site loss
数据库、控制面、接入层和本地备份一起不可用
historical obligation
必须证明某个历史版本可恢复、可查询或已按政策删除不同损失的 truth source 不同。误删一行时,最可信的边界可能来自审计事件; 存储毁坏时,最可信的边界是仓库中最后完整基础备份与连续 WAL;区域故障时, 本地仓库本身不再是可用前提。
场景一:误删与逻辑破坏
典型事件:
DELETE FROM orders;
UPDATE account SET balance = 0;
DROP TABLE customer;
deploy buggy migration;
application writes wrong currency or tenant id;主库、流复制副本和同步副本都会忠实重放这些操作。HA 可能保持服务在线, 却更快地把错误复制到每份在线副本。
误删恢复要先问:
- 错误事务的开始、提交和影响范围是什么?
- 目标是整个集群回退,还是只取回一张表/一批行?
- 目标时刻之后有哪些正确交易不能丢?
- 如何把旧事实与当前事实合并?
- 序列、外键、触发器、审计与外部系统如何一致?
常见安全路径不是“原地 PITR 回退生产”,而是:
restore historical cluster in isolation
-> validate target boundary
-> export affected logical set
-> compare with current production
-> reviewed merge or repair原地回退会同时删除误操作之后的所有正确提交,通常扩大损失。
场景二:介质、文件系统或集群丢失
典型事件:
data volume lost
filesystem corruption
all HA members share failed storage
operator removes whole cluster
encryption layer/key unavailable
malware changes data and online copies这时要恢复的是整个物理集群。所需链条通常为:
compatible PostgreSQL runtime
+ one usable base backup
+ every required WAL segment
+ timeline history
+ tablespace/path mapping
+ repository credential and encryption key
+ configuration and identity material注意最后两项不一定在 PostgreSQL 物理备份内。官方连续归档文档明确提醒,
postgresql.conf、pg_hba.conf 与 pg_ident.conf 的手工修改不由 WAL
恢复。平台声明、证书、服务发现、KMS/IAM 与 DNS 也需要独立保存。
场景三:区域或控制域故障
区域故障不是“把同一恢复命令放到另一台机器”:
source region unavailable
local object store unavailable
local DNS/control plane unavailable
operator identity federation degraded
secrets/KMS endpoint unavailable
network dependency changed
capacity not pre-provisioned如果数据库与备份仓库在同一机房、云账户、管理员凭据或密钥域,所谓 “远程对象存储”仍可能是共同故障。
区域场景的前提矩阵至少包括:
| 依赖 | 源域丢失时是否仍可用 | 证据 |
|---|---|---|
| 基础备份 | 是/否 | 异地对象清单、定期读取 |
| WAL | 是/否 | 最大已复制 WAL、延迟 |
| 解密密钥 | 是/否 | 独立保管与 break-glass 测试 |
| PostgreSQL 包/镜像 | 是/否 | 固定版本仓库 |
| Pigsty 声明 | 是/否 | 版本化、离站 |
| DNS/证书 | 是/否 | 独立控制面 |
| 应用依赖 | 是/否 | DR dependency map |
| 人员权限 | 是/否 | 异地登录演练 |
只有“对象在另一个 bucket”远远不够。
场景四:合规留存与法律冻结
合规问题不等于“把备份留久一点”。需要先区分:
operational recovery retention
为误删、故障与日常恢复保留
historical record retention
为审计、诉讼、监管或业务档案保留
legal hold
正常过期规则暂时不能删除特定材料
right-to-delete / minimization
到期后必须删除或不可再识别物理备份以整个 cluster 为粒度,单个数据主体的删除很难立刻传播到所有历史 备份。合规方案可能需要:
- 受控的短期物理恢复窗口;
- 更长期、字段经过选择与脱敏的逻辑档案;
- 按租户/数据域分离;
- 密钥销毁策略;
- legal hold 与正常过期的冲突处理;
- 恢复访问的审批、审计和二次删除。
不要让 DBA 独自解释法律语义。数据 owner、安全、法务与平台团队要共同签署 retention class。
场景卡,而不是一句“灾备”
为每个场景写同一张卡:
id: accidental-delete-orders
trigger: confirmed destructive transaction against orders
authoritative_boundary:
source: audit event + restore point
timezone: UTC
scope: selected tenant and time range
maximum_tolerated_loss:
committed_correct_orders: zero
service_target:
historical cluster queryable: 60 minutes
repaired rows in production: 4 hours
dependencies:
- base backup and continuous WAL
- audit event identity
- isolated restore capacity
- reviewed logical merge procedure
stop_conditions:
- target transaction ambiguous
- WAL gap
- post-target correct orders cannot be reconstructed
proof:
- restored marker boundary
- row and amount reconciliation
- approved repair change
owner: data-platform + orders-domainrecovery-scenarios.json 提供本章四类机器可读样例。没有 stop_conditions
的 runbook 很容易在压力下把“不知道”伪装成“应该没问题”。
同一个事故可能需要两条恢复路径
例如误删订单:
path A: service continuity
current production remains online
path B: historical reconstruction
isolated PITR -> export -> compare -> repair例如区域故障:
path A: promote designed DR replica
lower RTO, possibly different RPO/consistency contract
path B: restore off-site backup
slower, but independent historical recoveryHA、DR 与 backup 可以组合,但不能互相替名。
21.1.2 RPO、RTO、恢复粒度与保留周期
RPO:允许回退多远
令:
t_loss 事故发生或最后可信事实时间
t_recover 实际可恢复边界时间口径可写为:
[ RPO_{actual}=t_{loss}-t_{recover} ]
但它不是简单的 backup interval:
daily base backup + continuous WAL
RPO 主要由 WAL 最后耐久位置决定,不是 24 小时
daily logical dump only
RPO 可能接近 24 小时
streaming replica
对主机故障可能接近复制延迟
对误删可能是 0 秒地复制了错误,无法提供历史点生产 RPO 必须带场景:
primary process loss RPO
single-AZ loss RPO
repository outage RPO
operator logical error RPO
region loss RPO“RPO = 5 分钟”却不说明事故类型,是不完整的合同。
WAL archive 的 RPO
连续归档场景中,粗略地:
[ RPO_{archive} \approx t_{failure}-t_{last\ durable\ independent\ WAL} ]
需要强调 durable 与 independent:
- WAL 在主库
pg_wal中,不代表主机丢失后仍可取; - archive command 返回成功,不代表对象存储副本已跨域;
- 对象写入完成,不代表 KMS/credential 在事故中可用;
pg_stat_archiver的累计失败数不等于“当前失败”,要看最近成功/失败时序 和 backlog。
低流量系统还会遇到 segment 未填满。archive_timeout 或显式
pg_switch_wal() 可以缩短时间窗口,但过短会制造大量完整长度的归档对象。
RTO:恢复到哪种能力
完整恢复时间不是 restore 命令运行时间:
[ RTO_{\text{business}} \ge T_{\text{detect}} +T_{\text{decide}} +T_{\text{provision}} +T_{\text{retrieve}} +T_{\text{replay}} +T_{\text{validate}} +T_{\text{cutover}} +T_{\text{dependency}} ]
可以同时定义多个里程碑:
| 里程碑 | 完成条件 |
|---|---|
| repository selected | 备份、WAL、key 和 target 已审核 |
| bytes restored | pgBackRest 文件阶段完成 |
| consistent | PostgreSQL 到达一致恢复点 |
| read-only | 可以接受只读查询 |
| promoted | pg_is_in_recovery() = false |
| data validated | 引擎与业务不变量通过 |
| service cut over | 受控端点连接到正确角色 |
| backlog cleared | 积压与补偿完成 |
本章正式实验测到:
restore copy 2.758 s
start -> first read-only 0.963 s
start -> promoted 1.319 s若只记录前两个数字,会漏掉验证、路由、应用依赖和积压,也会把只读窗口误报为 “恢复完成”。
恢复粒度
至少有四种粒度:
cluster
物理备份/PITR 常见粒度;包含 cluster 内所有数据库
database/schema/table
逻辑导出或从隔离物理恢复中再导出
row/business object
比较、审计、补偿与应用语义
service capability
read-only、read-write、reporting、limited tenant 等PostgreSQL 物理 WAL 是 cluster 级历史。不能告诉恢复引擎“只重放某张表的 正确事务”。pgBackRest 的 database include/排除能力也不是任意表级 PITR; 选择性恢复的语义必须认真核对限制。
target 精度不等于 truth 精度
PostgreSQL 可按:
time
transaction ID
LSN
named restore point
immediate consistency
end of available WAL停止恢复。但技术 target 再精确,如果业务事件不明确,也不能证明正确。
例如:
10:00:00 application request sent
10:00:01 database transaction committed
10:00:02 external payment confirmed
10:00:03 audit pipeline wrote event“恢复到 10:00:02”到底包含什么,取决于时区、提交时间、inclusive 语义与 外部系统。命名点可以降低实验歧义,但生产误删通常只能事后从日志、XID、 LSN 或审计重建边界。
保留周期不是 PITR 窗口的同义词
假设“保留 14 天全备”,仍不能自动推出:
过去 14 天每一秒都可恢复还需要:
- 至少一份在目标之前结束的可用基础备份;
- 从该备份起到目标的连续 WAL;
- timeline history;
- 未丢失的依赖备份;
- 可用的解密密钥和仓库权限;
- 与目标 PostgreSQL 版本兼容的恢复环境。
真实 PITR window 是这些集合的交集。
恢复点频度、版本数与日历跨度
三个常被混淆的量:
capture cadence
多久产生一个新逻辑/物理基线
version count
保留多少组 backup
calendar coverage
最旧可恢复事实距现在多远retention_full=2 按 count 解释,与按 time 解释完全不同;差异/增量依赖还会
影响一组备份何时能安全过期。应把政策写成可测试的例子:
at 2026-08-01 12:00 UTC
must restore any named daily checkpoint since 2026-07-18
must restore any WAL time since 2026-07-25
must retain month-end logical archive for 7 years然后定期真的选最旧目标恢复。
分级服务目标
不是所有数据库都应买同一种恢复成本:
| tier | 场景示例 | RPO | RTO | 机制 |
|---|---|---|---|---|
| critical | 支付主账 | 秒/分钟级 | 分钟级 | HA + 连续 WAL + 异地仓库 + 高频演练 |
| important | 订单业务 | 分钟级 | 小时级 | 连续 WAL + 物理备份 + 选择性恢复 |
| standard | 内部应用 | 小时级 | 当日 | 日备 + 合理 WAL/逻辑导出 |
| reconstructable | 派生分析 | 可重算 | 天级 | 上游真相 + schema/code + optional backup |
分级的结果不是降低严谨性,而是让成本与真实损失匹配。
目标反推设计
对每个 tier 反推:
RPO
-> WAL archive latency / dump cadence / replication mode
RTO
-> restore bandwidth / provisioned capacity / runbook automation
granularity
-> physical, logical, audit or application repair path
retention
-> backup dependency graph + WAL policy + legal hold
failure domain
-> repository placement + credential + key independence
proof
-> drill frequency + oldest target + business invariant这比先决定“全备还是增量”更接近工程问题。
21.1.3 逻辑、物理、快照和副本的职责边界
没有一种副本解决全部恢复问题
| 机制 | 主要内容 | 典型粒度 | 优势 | 主要盲区 |
|---|---|---|---|---|
pg_dump | SQL/归档格式逻辑对象 | database/object | 可选择、可迁移、可检查 | 慢;非 cluster 物理 PITR |
pg_dumpall | 全局对象与多个库的 SQL | cluster logical | roles/tablespaces 辅助 | 大规模恢复慢;无 WAL |
| physical base backup | 数据文件物理状态 | cluster | 快、完整、可配 WAL | 版本/平台耦合;粗粒度 |
| WAL archive | 变更历史 | cluster | PITR、连续恢复 | 必须连续;依赖 base |
| storage snapshot | block/volume | volume | 快速 capture/clone | 一致性、跨卷、WAL 与快照语义 |
| streaming replica | 在线物理历史尾部 | cluster | 低 RTO、读扩展 | 复制误删;非独立历史 |
| logical replication/CDC | 选择性变更流 | table/event | 异构、下游重建 | DDL/序列/删除/slot 等边界 |
设计的关键是组合,而不是选一个“最好”的工具。
逻辑备份适合什么
pg_dump 在一个一致性快照中读取数据库,可以:
- 选择 schema/table;
- 以 custom/directory 格式并行恢复;
- 跨部分 PostgreSQL 版本迁移;
- 检视 DDL 与数据;
- 从隔离 PITR 中导出被误删的对象。
但逻辑备份:
- 不是数据目录文件副本;
- 不含用于物理重放的控制信息;
- 不能接到 WAL archive 上继续重放;
- 通常不覆盖所有 cluster-wide 配置与运行状态;
- 大库导出/导入和索引重建可能远慢于物理恢复;
- 恢复顺序、owner、extension、privilege 与 external dependency 仍要处理。
PostgreSQL 官方文档明确说明 pg_dump/pg_dumpall 不能作为连续归档物理
恢复链的一部分。
物理备份适合什么
物理备份复制 cluster 数据文件,并依靠 WAL 把不一致的文件时间切片恢复到 一致点。它保留:
all databases in cluster
catalogs and physical relation state
transaction status
extension physical objects inside cluster
system identifier lineage它适合:
- 大型数据库快速整库恢复;
- PITR;
- 创建 standby/clone;
- 保留完整引擎语义。
但通常要求同一大版本与兼容架构/页格式,并以 cluster 为恢复单位。物理 恢复后再做逻辑选择,是误删恢复常见组合。
PostgreSQL 18 原生增量不要和工具增量混为一谈
PostgreSQL 18 的 pg_basebackup --incremental 依赖:
earlier backup manifest
WAL summaries covering the required LSN interval
pg_combinebackup at restore preparation
all earlier dependent backups
normal WAL recovery requirements这是一套 PostgreSQL 原生机制。pgBackRest 的 full/diff/incr、block incremental、bundle 与 repository metadata 是另一套实现和依赖图。
讨论“我们做增量备份”时,必须说清:
which tool
which version
dependency chain
restore assembly step
expiration owner不能因为术语相同就互换 runbook。
快照要回答一致性
单卷快照可能在 block 层原子,但数据库语义还要问:
- PostgreSQL 是否运行中?
- 数据、WAL、tablespace 是否跨多个卷?
- 各卷快照是否同一 consistency group?
- 是否调用
pg_backup_start/stop或工具协议? - 快照克隆后如何恢复与识别 timeline?
- 快照控制面与主存储是否共同故障?
- 是否定期挂载并启动验证?
“云盘快照成功”不是数据库一致性的证据。一个 crash-consistent snapshot 也许能通过 crash recovery,但不能自动提供任意 PITR 或跨卷一致性。
Replica 为什么不是 backup
流复制的目标是把当前历史低延迟复制到其他实例:
primary DELETE wrong rows
-> WAL
-> replica replays same DELETE它可以:
- 降低实例/主机故障的 RTO;
- 在同步策略下改善某些故障的 RPO;
- 分担读;
- 作为备份来源,降低主库读取压力。
它不能单独:
- 保留误操作之前的历史点;
- 对抗影响全部成员的错误配置;
- 对抗同一账户/区域/恶意管理员;
- 证明长期 retention;
- 替代隔离恢复。
延迟副本可增加逻辑错误反应窗口,但依然需要:
delay guarantee
pause/stop authority
monitoring that does not auto-heal away the delay
protection from direct reads/writes
independent backup for other failuresCDC 不是免费备份
事件流或 logical replication 能帮助重建某些数据,但要验证:
- 初始快照从哪里来;
- DDL 是否同步;
- TRUNCATE/DELETE 是否保留足够语义;
- sequence、large object 与 extension state;
- slot 丢失或 lag;
- exactly-once 是否只是消费端幂等;
- 下游是否共享同一个逻辑错误。
它更适合作为另一条可重建历史,而不是未经证明的整库恢复替代品。
一份常见的组合
HA replicas
handle selected availability failures
physical full/diff/incr + WAL
handle cluster restore and PITR
logical export
handle object-level portability and long-term selected records
audit/event history
identify business boundary and support repair
off-site immutable copy
handle control-domain and destructive repository loss
restore drills
prove that all of the above compose“多种机制”不等于重复浪费;前提是每种都有明确场景和 owner。
选择问题
面对一个需求,按顺序问:
1. loss is logical, physical, site-wide, or compliance?
2. recover whole cluster or selected facts?
3. need historical point or latest state?
4. how independent must the copy be?
5. what PostgreSQL/version compatibility exists?
6. what target precision can be observed?
7. how will business correctness be proven?
8. what is the controlled return-to-service path?可能的答案:
selected rows from yesterday
isolated physical PITR -> logical export -> reviewed merge
whole cluster after storage loss
physical backup + WAL -> replacement infrastructure
cross-major migration
logical dump/restore or logical replication, not physical replay
low-RTO host failure
HA promotion, while independent backup remains separate
seven-year selected archive
policy-specific logical record, not indefinite operational WAL by default本节验收问题
在进入物理原理前,应能回答:
- 你的前三个恢复场景是什么?
- 每个场景的 authoritative boundary 来自哪里?
- RPO/RTO 的起止点和完成能力是什么?
- 哪些损失会被 replica 同步复制?
- 当前 retention 是否真的形成连续 PITR window?
- 配置、密钥、声明和应用依赖存放在哪里?
- 哪些结论只有实际 restore 才能证明?
如果答案仍是“每天有备份”,还没有完成设计。
小结
恢复体系从业务损失开始:
scenario defines truth
truth defines target
target defines mechanism
mechanism defines dependencies
dependencies define retention and placement
proof defines the drill下一节进入物理链条:一份在线基础备份为什么能够是不一致的文件切片, WAL 又如何把它变成一个可选择时间点的 PostgreSQL 历史。
返回本章目录 · 下一节:物理备份与 WAL 连续性 · 查看全书目录 · 查看索引中心