# 从恢复场景设计备份

LLMS 索引： [llms.txt](/llms.txt)

---

备份设计最容易犯的错误，是从一个漂亮的日历开始：

```text
周日全备
周一到周六增量
保留 14 天
```

它回答了“工具何时运行”，却没有回答：

```text
什么会丢？
丢了什么算业务损失？
要回到哪个边界？
由谁判断边界正确？
依赖也丢失时怎么办？
多快恢复到什么能力？
```

正确顺序是从场景与真相出发，再选择恢复机制、保留和调度。

## 21.1.1 误删、介质故障、区域故障与合规留存 {#item-21-1-1}

### 先定义“损失”

数据库恢复不是把字节放回磁盘，而是重新建立被业务接受的事实。至少区分：

```text
logical loss
  数据库仍运行，但某些正确事实被删除、覆盖或错误变更

physical loss
  数据文件、WAL、设备或整个集群不可用/不可信

site loss
  数据库、控制面、接入层和本地备份一起不可用

historical obligation
  必须证明某个历史版本可恢复、可查询或已按政策删除
```

不同损失的 truth source 不同。误删一行时，最可信的边界可能来自审计事件；
存储毁坏时，最可信的边界是仓库中最后完整基础备份与连续 WAL；区域故障时，
本地仓库本身不再是可用前提。

### 场景一：误删与逻辑破坏

典型事件：

```sql
DELETE FROM orders;
UPDATE account SET balance = 0;
DROP TABLE customer;
deploy buggy migration;
application writes wrong currency or tenant id;
```

主库、流复制副本和同步副本都会忠实重放这些操作。HA 可能保持服务在线，
却更快地把错误复制到每份在线副本。

误删恢复要先问：

1. 错误事务的开始、提交和影响范围是什么？
2. 目标是整个集群回退，还是只取回一张表/一批行？
3. 目标时刻之后有哪些正确交易不能丢？
4. 如何把旧事实与当前事实合并？
5. 序列、外键、触发器、审计与外部系统如何一致？

常见安全路径不是“原地 PITR 回退生产”，而是：

```text
restore historical cluster in isolation
  -> validate target boundary
      -> export affected logical set
          -> compare with current production
              -> reviewed merge or repair
```

原地回退会同时删除误操作之后的所有正确提交，通常扩大损失。

### 场景二：介质、文件系统或集群丢失

典型事件：

```text
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
```

这时要恢复的是整个物理集群。所需链条通常为：

```text
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 也需要独立保存。

### 场景三：区域或控制域故障

区域故障不是“把同一恢复命令放到另一台机器”：

```text
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”远远不够。

### 场景四：合规留存与法律冻结

合规问题不等于“把备份留久一点”。需要先区分：

```text
operational recovery retention
  为误删、故障与日常恢复保留

historical record retention
  为审计、诉讼、监管或业务档案保留

legal hold
  正常过期规则暂时不能删除特定材料

right-to-delete / minimization
  到期后必须删除或不可再识别
```

物理备份以整个 cluster 为粒度，单个数据主体的删除很难立刻传播到所有历史
备份。合规方案可能需要：

- 受控的短期物理恢复窗口；
- 更长期、字段经过选择与脱敏的逻辑档案；
- 按租户/数据域分离；
- 密钥销毁策略；
- legal hold 与正常过期的冲突处理；
- 恢复访问的审批、审计和二次删除。

不要让 DBA 独自解释法律语义。数据 owner、安全、法务与平台团队要共同签署
retention class。

### 场景卡，而不是一句“灾备”

为每个场景写同一张卡：

```yaml
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-domain
```

`recovery-scenarios.json` 提供本章四类机器可读样例。没有 `stop_conditions`
的 runbook 很容易在压力下把“不知道”伪装成“应该没问题”。

### 同一个事故可能需要两条恢复路径

例如误删订单：

```text
path A: service continuity
  current production remains online

path B: historical reconstruction
  isolated PITR -> export -> compare -> repair
```

例如区域故障：

```text
path A: promote designed DR replica
  lower RTO, possibly different RPO/consistency contract

path B: restore off-site backup
  slower, but independent historical recovery
```

HA、DR 与 backup 可以组合，但不能互相替名。

## 21.1.2 RPO、RTO、恢复粒度与保留周期 {#item-21-1-2}

### RPO：允许回退多远

令：

```text
t_loss     事故发生或最后可信事实时间
t_recover  实际可恢复边界
```

时间口径可写为：

\[
RPO_{actual}=t_{loss}-t_{recover}
\]

但它不是简单的 backup interval：

```text
daily base backup + continuous WAL
  RPO 主要由 WAL 最后耐久位置决定，不是 24 小时

daily logical dump only
  RPO 可能接近 24 小时

streaming replica
  对主机故障可能接近复制延迟
  对误删可能是 0 秒地复制了错误，无法提供历史点
```

生产 RPO 必须带场景：

```text
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 | 积压与补偿完成 |

本章正式实验测到：

```text
restore copy                  2.758 s
start -> first read-only      0.963 s
start -> promoted             1.319 s
```

若只记录前两个数字，会漏掉验证、路由、应用依赖和积压，也会把只读窗口误报为
“恢复完成”。

### 恢复粒度

至少有四种粒度：

```text
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 可按：

```text
time
transaction ID
LSN
named restore point
immediate consistency
end of available WAL
```

停止恢复。但技术 target 再精确，如果业务事件不明确，也不能证明正确。

例如：

```text
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 天全备”，仍不能自动推出：

```text
过去 14 天每一秒都可恢复
```

还需要：

- 至少一份在目标之前结束的可用基础备份；
- 从该备份起到目标的连续 WAL；
- timeline history；
- 未丢失的依赖备份；
- 可用的解密密钥和仓库权限；
- 与目标 PostgreSQL 版本兼容的恢复环境。

真实 PITR window 是这些集合的交集。

### 恢复点频度、版本数与日历跨度

三个常被混淆的量：

```text
capture cadence
  多久产生一个新逻辑/物理基线

version count
  保留多少组 backup

calendar coverage
  最旧可恢复事实距现在多远
```

`retention_full=2` 按 count 解释，与按 time 解释完全不同；差异/增量依赖还会
影响一组备份何时能安全过期。应把政策写成可测试的例子：

```text
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 反推：

```text
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 逻辑、物理、快照和副本的职责边界 {#item-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 把不一致的文件时间切片恢复到
一致点。它保留：

```text
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` 依赖：

```text
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 是另一套实现和依赖图。

讨论“我们做增量备份”时，必须说清：

```text
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

流复制的目标是把当前历史低延迟复制到其他实例：

```text
primary DELETE wrong rows
  -> WAL
      -> replica replays same DELETE
```

它可以：

- 降低实例/主机故障的 RTO；
- 在同步策略下改善某些故障的 RPO；
- 分担读；
- 作为备份来源，降低主库读取压力。

它不能单独：

- 保留误操作之前的历史点；
- 对抗影响全部成员的错误配置；
- 对抗同一账户/区域/恶意管理员；
- 证明长期 retention；
- 替代隔离恢复。

延迟副本可增加逻辑错误反应窗口，但依然需要：

```text
delay guarantee
pause/stop authority
monitoring that does not auto-heal away the delay
protection from direct reads/writes
independent backup for other failures
```

### CDC 不是免费备份

事件流或 logical replication 能帮助重建某些数据，但要验证：

- 初始快照从哪里来；
- DDL 是否同步；
- TRUNCATE/DELETE 是否保留足够语义；
- sequence、large object 与 extension state；
- slot 丢失或 lag；
- exactly-once 是否只是消费端幂等；
- 下游是否共享同一个逻辑错误。

它更适合作为另一条可重建历史，而不是未经证明的整库恢复替代品。

### 一份常见的组合

```text
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。

### 选择问题

面对一个需求，按顺序问：

```text
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?
```

可能的答案：

```text
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
```

### 本节验收问题

在进入物理原理前，应能回答：

1. 你的前三个恢复场景是什么？
2. 每个场景的 authoritative boundary 来自哪里？
3. RPO/RTO 的起止点和完成能力是什么？
4. 哪些损失会被 replica 同步复制？
5. 当前 retention 是否真的形成连续 PITR window？
6. 配置、密钥、声明和应用依赖存放在哪里？
7. 哪些结论只有实际 restore 才能证明？

如果答案仍是“每天有备份”，还没有完成设计。

## 小结

恢复体系从业务损失开始：

```text
scenario defines truth
truth defines target
target defines mechanism
mechanism defines dependencies
dependencies define retention and placement
proof defines the drill
```

下一节进入物理链条：一份在线基础备份为什么能够是不一致的文件切片，
WAL 又如何把它变成一个可选择时间点的 PostgreSQL 历史。

---

[返回本章目录](../) · [下一节：物理备份与 WAL 连续性](../02/) ·
[查看全书目录](/toc/) · [查看索引中心](/indexes/)
