主题
备份恢复与运维:备份的价值只在恢复那一刻
一句话:备份的目标不是「有一份文件」,而是在约定的 RPO/RTO 内把库恢复到某个一致点;逻辑备份管对象,物理备份 + WAL 归档管时间点。
适用 PostgreSQL 16/17。命令中的主机、路径、库名按环境替换。
为什么需要
误删表、误更新、磁盘损坏、机房故障,对应的恢复手段不同:
- 误删一张表:逻辑备份或 PITR 到误删前。
- 整个实例损坏:基础备份 + WAL 归档。
- 主库宕机:流复制切换(见 03 篇),不是备份。
没有恢复演练的备份不可信。备份文件存在、命令返回 0,都不等于能在目标 RTO 内恢复。
核心机制
逻辑备份与物理备份
| 维度 | 逻辑备份(pg_dump) | 物理备份(pg_basebackup) |
|---|---|---|
| 内容 | SQL/自定义格式的对象和数据 | 整个数据目录的文件块 |
| 粒度 | 库、schema、表 | 整个实例 |
| 跨版本 | 通常可以(大版本差异需验证) | 必须同大版本 |
| 恢复速度 | 慢,要重新执行 SQL/建索引 | 快,文件级恢复 |
| PITR | 不支持 | 支持(配合 WAL 归档) |
| 典型用途 | 单表恢复、迁移、小库 | 大库全量恢复、搭备库 |
pg_dump 在导出时使用一致性快照,并持有 ACCESS SHARE 锁;它不阻塞 DML,但长备份会阻塞 DDL。--no-sync 只是不等待输出文件刷盘,不是「不保证一致性」,不要混淆。
pg_basebackup 会触发检查点,然后流式复制数据文件;-X stream 同时收集备份期间产生的 WAL。备份期间数据库可读写,但检查点可能带来 IO 尖刺,--checkpoint=fast 可以缩短备份时间、加大尖刺。
WAL 归档与 PITR
PITR(Point-In-Time Recovery)需要三个要素:
- 一个基础备份(
pg_basebackup或 pgBackRest 的全量备份)。 - 从备份开始到目标时间点的连续 WAL 归档。
- 恢复目标:时间、LSN、XID 或命名还原点。
恢复时,PostgreSQL 从基础备份的 backup_label 确定起点,用 restore_command 不断取 WAL 重放,直到 recovery_target_*。每次恢复/提升会生成新的时间线(timeline),防止新 WAL 和旧历史混淆。
PostgreSQL 12+ 不再使用 recovery.conf:把恢复参数写进 postgresql.conf(或 postgresql.auto.conf),用 recovery.signal 触发恢复。standby.signal 则表示进入持续 standby 模式。
RPO 与 RTO
- RPO(Recovery Point Objective):能容忍丢失多长时间的数据。异步复制下,未传到备库的 WAL 就是 RPO;每日逻辑备份的 RPO 约 24 小时。
- RTO(Recovery Time Objective):从故障到恢复服务的时间。取决于检测、切换、恢复数据量、重放 WAL、应用重连。
| 方案 | RPO | RTO(经验值) |
|---|---|---|
| 每日逻辑备份 | 约 24 小时 | 小时级,取决于库大小和索引重建 |
| 基础备份 + WAL 归档 | 约一个 WAL 切换周期 | 分钟到小时 |
| 异步流复制 + 自动切换 | 秒级到分钟级 | 数十秒 |
| 同步流复制 + 自动切换 | 0(同步备库确认后) | 数十秒,依赖 quorum |
RTO 数字必须用真实库演练,不能照搬。
最小上手
逻辑备份与恢复
bash
# 自定义格式:压缩、支持并行恢复、可选择性恢复
pg_dump -h 127.0.0.1 -p 5432 -U postgres -Fc -f /backup/app.dump appdb
# 并行恢复(自定义/目录格式)
pg_restore -h 127.0.0.1 -U postgres -d appdb_restore -j 4 /backup/app.dumppg_dump -Fd -j 是并行导出;pg_restore -j 是并行恢复。纯 SQL 格式(-Fp)只能 psql -f 顺序执行,不支持 pg_restore -j。
物理备份
bash
# 基础备份到空目录,同时流式收集 WAL
pg_basebackup -h 10.0.0.11 -U replicator \
-D /backup/base/20261024 -Fp -Xs -P
# 校验基础备份(PostgreSQL 13+,需要 backup_manifest)
pg_verifybackup /backup/base/20261024pg_basebackup 的目标目录必须为空。-Ft 生成 tar 包,-Fp 生成普通目录;-R 会顺便写好 standby.signal 和 primary_conninfo,适合直接搭备库。PostgreSQL 17 起支持增量基础备份(pg_basebackup --incremental),更早版本需要 pgBackRest/Barman 做增量。
归档与 PITR
ini
# postgresql.conf
wal_level = replica
archive_mode = on
archive_command = 'test ! -f /wal_archive/%f && cp %p /wal_archive/%f'
archive_timeout = 60archive_mode 需要重启;archive_command 可以重载。test ! -f 防止覆盖已归档的 WAL。
恢复步骤(PostgreSQL 12+):
bash
# 1. 停库,保护现场,清空/移走原数据目录
pg_ctl -D /var/lib/pgsql/16/data stop
# 2. 还原基础备份到数据目录(-Fp 直接复制目录内容)
cp -a /backup/base/20261024/. /var/lib/pgsql/16/data/
# 3. 创建恢复信号
touch /var/lib/pgsql/16/data/recovery.signal
# 4. 在 postgresql.conf 配置 restore_command 和 recovery_target_time
# (可选 recovery_target_action = 'promote')
# 5. 启动并观察日志
pg_ctl -D /var/lib/pgsql/16/data start验证:SELECT pg_is_in_recovery(); 提升后应为 f;SELECT timeline_id FROM pg_control_checkpoint(); 恢复后时间线会递增。
生产动作
- 备份策略:每周全量 + 每日增量/差异 + 连续 WAL 归档;WAL 保留时间必须覆盖最老的基础备份。pgBackRest 用
--type=full/incr/diff、--repo1-retention-full;Barman 用barman backup/barman check。 - 恢复演练:至少每季度在独立实例做一次 PITR,记录实际 RTO;只跑
pg_verifybackup不够,它验证文件完整性,不验证业务可用性。 - 归档监控:
pg_stat_archiver看failed_count、last_archived_time、last_failed_time;归档失败会导致 PITR 在缺口处停下。 - 逻辑备份的一致性:
pg_dump默认使用一致性快照;需要更严格的串行化语义时用--serializable-deferrable,但它可能等待。 - 大库恢复:先恢复基础备份,再重放 WAL;恢复期间不要对外开放连接。恢复后
ANALYZE,并检查序列值、扩展、权限。 - 不要用备库代替备份:误删会同步到备库;备库只解决机器/磁盘故障,不解决逻辑错误。
四类高频运维故障
- 磁盘只涨不降、DELETE 后不回收 →
pg_stat_user_tables的n_dead_tup/n_live_tup和last_autovacuum→ Vacuum 追不上 → 按表调小autovacuum_vacuum_scale_factor(默认 0.2),必要时pg_repack,不要高峰做VACUUM FULL。详见 11 篇。 - 数据库突然只读、报事务 ID 相关错误 →
SELECT age(datfrozenxid) FROM pg_database;→ 接近 2 亿(autovacuum_freeze_max_age)→ 立即安排VACUUM FREEZE,不要靠重启硬扛。 - 报
too many clients→pg_stat_activity按state计数对比max_connections(默认 100)→ 连接泄漏或池配置错误 → 修连接池,不要只调大上限。详见 08 篇。 - 迁移后某些查询反而更慢 →
EXPLAIN (ANALYZE, BUFFERS)对比 rows 预估与实际 → 统计信息过期或索引不匹配 →ANALYZE、建扩展统计/复合索引。JSONB 查询慢则确认是否用了@>而不是->>。详见 05、08 篇。
边界
- 逻辑备份不能做 PITR:它只有导出时刻的快照,没有连续 WAL。
- 物理备份不能跨大版本:数据文件不兼容;跨版本用逻辑备份/逻辑复制/
pg_upgrade。 pg_basebackup不是增量(17 之前):大库每天全量成本高,用 pgBackRest/Barman;17+ 的增量也要配合pg_combinebackup使用。- WAL 归档断了,PITR 就断了:缺一个 WAL 段,恢复会在那里停下;归档失败必须告警。
- 没有演练的备份不算备份:文件存在、校验通过,都不等于能在 RTO 内恢复业务。
- 不要删除比最老基础备份更早的 WAL:否则那份备份无法恢复到一致点。
讲清楚
备份分两条线:逻辑备份用 pg_dump 导出对象和数据,适合单表恢复、迁移、小库;物理备份用 pg_basebackup 复制整个数据目录,配合 WAL 归档才能做 PITR。PITR 的三要素是基础备份、连续 WAL、恢复目标;PostgreSQL 12+ 用 recovery.signal 触发恢复,不再有 recovery.conf。RPO 决定最多丢多少数据:每日逻辑备份约 24 小时,异步复制是未传 WAL,同步复制可以做到 0;RTO 决定多久恢复,必须用真实库演练。运维上最该盯的是 WAL 归档失败、死元组比例、事务 ID 回卷余量和连接数。最后记住:备库不是备份,误删会同步;没有恢复演练的备份,只是磁盘上的一份文件。