#DevOps

PostgreSQL 复制与备份策略

数据是命,丢了就完。本文讲清 PostgreSQL 的高可用与灾备:流复制(主从)、同步 vs 异步、物理备份(pg_basebackup/WAL 归档)、逻辑复制、以及一套"3-2-1 备份"策略和恢复演练,让你的数据库扛得住故障。

本文要点

  • 主题聚焦:DevOps
  • 适合人群:独立开发者、全栈工程师、AI 应用开发者
  • 核心价值:快速理解背景、实践路径、常见误区和工程建议
作者 @admin·发布于 2026年5月21日·更新于 2026年7月23日

PostgreSQL 复制与备份策略:数据是命,备份是命根

我见过太多团队的故事是这样的:业务跑得好好的,某天夜里主库磁盘炸了,运维一查——备份上周就没成功过,因为磁盘满了脚本静默失败;或者某个新来的实习生在生产库 DELETE FROM users WHERE id = 123; 漏了 WHERE,一个回车下去,全表没了。等 DBA 赶到现场,只能面对一堆哭丧着脸的产品经理。

数据库宕机只是"难受",数据丢了才是"完蛋"。硬件会坏(SSD 有写入寿命、磁道会老化、机房会断电、网线会被保洁阿姨拔了),人会犯错(误删库、误删表、误改数据、跑了不该跑的迁移脚本),还有勒索软件、地震、洪水。高可用(HA)解决的是"还能用",灾备(DR)解决的是"还能找回数据",两者不能互相替代。

PostgreSQL 在复制和备份这块生态已经相当成熟。这篇文章我把实战中最关键的几条线一次讲透:流复制、同步/异步的取舍、物理备份与 PITR、逻辑复制、3-2-1 策略,以及最容易被忽略、却最致命的一环——恢复演练。


一、流复制:主库的每一次改动,从库都跟得上

PostgreSQL 的复制底座是 WAL(Write-Ahead Logging,预写式日志)。任何事务在修改数据页之前,先把改动写到 WAL 文件里。流复制(Streaming Replication)做的事就是:主库把这些 WAL 流式推给从库,从库重放(replay)WAL,从而实现近乎实时的物理同步。

这是物理复制——按字节复制,从库和主库长得一模一样,连内部数据页布局都一致。优点是通用、高效、对应用透明;缺点是从库只能是整个集群级别的复制,不能挑表。

一个典型的主从架构:

text
        ┌──────────────┐   WAL stream   ┌──────────────┐
        │   Primary    │ ─────────────▶ │   Standby    │
        │  (读写主库)  │                │ (只读从库)   │
        └──────────────┘                └──────────────┘
               ▲                               │
               │ 应用写入                       │ 只读查询 / 故障切换
               │                               ▼
           ┌───┴───┐                      热备/报表
           | 客户端 |                      连接过来读
           └───────┘

主库的关键配置(postgresql.conf):

conf
wal_level = replica            # 至少 replica,逻辑复制要 logical
max_wal_senders = 10           # 允许几个从库连过来
wal_keep_size = 1024           # MB,为从库保留的 WAL,避免追不上
hot_standby = on               # 从库在恢复期间是否可读(配在从库上)
archive_mode = on              # 开启归档,见后文备份章节
archive_command = 'test ! -f /backup/wal/%f && cp %p /backup/wal/%f'

主库还要建一个有复制权限的角色:

sql
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'a-strong-secret';

并在 pg_hba.conf 里放行复制连接:

conf
host replication replicator 10.0.0.0/24 scram-sha-256

从库的初始化,用 pg_basebackup 从主库拉一份全量:

bash
pg_basebackup \
  -h primary.host -U replicator -D /var/lib/postgresql/data \
  -Fp -Xs -P -R

-R 会自动写一个 standby.signal 文件并配上 primary 连接信息,从库起来后就进入持续恢复模式,源源不断接收 WAL。

Failover(故障切换):主库挂了,把某个从库提升为新主库。PostgreSQL 10+ 用 pg_promote():

sql
SELECT pg_promote();  -- 在从库上执行,几秒内变成可写主库

生产环境别手动敲,用 Patroni / repmgr / pg_auto_failover 这类工具来托管选主、VIP/DNS 切换、脑裂防护。手撸 failover 脚本是新人最爱踩的坑——脑裂(split-brain)两个主库同时写,数据直接冲突烂掉。


二、同步 vs 异步:一致性和性能的单选题

流复制默认是异步的:主库写完 WAL、本地落盘就告诉客户端"提交成功",不等从库确认。这意味着:

  • 写延迟低,主库性能不受从库拖累;
  • 但如果主库刚提交完就炸了,而那部分 WAL 还没发到从库,这部分数据就丢了。

同步复制(synchronous_commit = on + synchronous_standby_names 配置)则是:主库必须等到至少一个同步从库确认收到 WAL 后,才向客户端返回成功。

conf
synchronous_standby_names = 'FIRST 1 (standby1, standby2)'

好处是强一致——主库 commit 成功的事务,从库一定有;坏处是写延迟变高,而且同步从库一旦全挂,主库会 hang 住(为了保护一致性,写入会卡住直到超时或从库恢复)。所以同步复制通常配 FIRST 1 (a, b),至少留一个备份候选。

维度异步复制同步复制
一致性最终一致强一致
写性能快慢(多一个 RTT)
丢数据风险可能丢少量最新事务几乎不丢
主库可用性不受从库影响同步从库全挂则写阻塞

怎么选? 我的实战经验:

  • 金融、交易、账本类:核心表走同步,丢一笔钱就是事故。
  • 内容、日志、行为流:异步足矣,丢最后几秒日志业务能接受,别为它牺牲性能。
  • 混合打法:PostgreSQL 支持按事务级别开关同步,关键操作临时 SET LOCAL synchronous_commit = on;,日常异步跑得飞快。

记住一句话:同步复制保的是 RPO(数据丢失量),牺牲的是 RTO(恢复时间)和性能,没有银弹。


三、物理备份:全量 + WAL 归档,能穿越回任意时间点

复制解决的是"现在还在跑",备份解决的是"过去能找回"。PostgreSQL 的物理备份组合拳是 pg_basebackup 全量 + WAL 归档增量,合起来能做 PITR(Point-in-Time Recovery,时间点恢复)——把数据库恢复到过去任意一秒。

text
   全量基础备份            持续归档的 WAL
   pg_basebackup    +     archive_command     =    PITR
   (某个时刻的快照)        (之后所有改动)         (恢复到任意时间点)

第一步:全量基础备份。在线热备,不锁库:

bash
pg_basebackup -D /backup/base -Ft -z -P -X fetch
# -Ft 打成 tar,-z 压缩,-X fetch 备份期间需要的 WAL 也带上

或者用 pgBackRest / Barman 这类专业工具,带并行、压缩、增量、远端管理,生产环境强烈推荐,别用裸 pg_basebackup 自己拼脚本。

第二步:持续 WAL 归档。archive_command 把主库产生的每个 WAL 段拷到独立存储(对象存储、NFS、异地):

conf
archive_command = 'aws s3 cp %p s3://my-pg-wal/%f'

WAL 文件名是单调递增的,所以归档天然有序。

第三步:做 PITR 恢复。假设周三下午 3 点 17 分有人误删了表,现在要恢复到 3 点 16 分:

  1. 把最近一次全量备份解压到新数据目录;
  2. 在数据目录放 recovery.signal;
  3. 配 postgresql.auto.conf:
conf
restore_command = 'aws s3 cp s3://my-pg-wal/%f %p'
recovery_target_time = '2026-07-17 15:16:00+08'
recovery_target_action = 'promote'
  1. 启动 PostgreSQL,它会回放所有归档 WAL,到目标时间停下并 promote 成新主库。

这个能力是物理备份的核心价值:不仅能恢复到"最近",还能精准恢复到"事故前一秒"。逻辑备份做不到这点。


四、逻辑备份与逻辑复制:灵活但不强求一致

逻辑备份用 pg_dump / pg_dumpall,导出的是 SQL 语句或自定义格式,跨版本、跨架构、能挑表挑 schema。适合迁移、升级前快照、把生产数据导一份到测试库。

bash
pg_dump -Fc -d mydb -f mydb.dump            # 自定义压缩格式,可并行恢复
pg_dump -d mydb -t users -t orders -f sub.dump   # 只导部分表
pg_restore -j 4 -d newdb mydb.dump          # 4 线程并行恢复

逻辑备份的硬伤:不能 PITR,只能恢复到 dump 那一刻;大库 dump 一次可能几小时,期间数据还在变。所以逻辑备份是补充,不能替代物理备份 + 归档。

逻辑复制(Logical Replication) 是另一回事,它工作在表级别,基于发布/订阅模型:主库 CREATE PUBLICATION 发布一组表的变更,目标库 CREATE SUBSCRIPTION 订阅。变更以逻辑格式(INSERT/UPDATE/DELETE)传过去,目标库重放。

sql
-- 发布端(主库)
CREATE PUBLICATION pub_users FOR TABLE users, orders;

-- 订阅端(另一个库,可以是不同 PG 实例)
CREATE SUBSCRIPTION sub_users
  CONNECTION 'host=primary.host user=rep password=...'
  PUBLICATION pub_users;

逻辑复制的杀手级场景:

  • 跨大版本升级:PG 13 → PG 16,逻辑复制几乎是零停机升级的标准方案;
  • 选择性复制:只把 users 表同步到分析库,不拖整个集群;
  • 异构分发:把数据拆到不同库给不同业务线;
  • 合并多库到数仓/数据湖。

代价是:逻辑复制不传 schema(表结构要先建好)、不支持序列、大事务有延迟、对主键强依赖(没主键的表 UPDATE/DELETE 会炸)。


五、3-2-1 备份策略:别把鸡蛋放一个篮子里

业界公认的备份铁律叫 3-2-1:

  • 3 份副本(生产数据 + 至少 2 份备份);
  • 2 种不同介质(比如对象存储 + 磁带,或云盘 + 本地 NAS);
  • 1 份完全离线/异地(防机房级灾难:火灾、洪水、区域性宕机)。

映射到 PostgreSQL 的实战配置:

text
                 ┌─────────────────────────┐
                 │   生产 PG 主库(副本 1)  │
                 └──────────┬──────────────┘
                            │ 流复制
                   ┌────────▼────────┐
                   │  异地只读从库    │ (副本 2,不同可用区)
                   └────────┬────────┘
                            │ pg_basebackup + WAL 归档
              ┌─────────────┼──────────────┐
              ▼             ▼              ▼
        对象存储 S3     本地 NAS        离线冷备
        (介质 A)      (介质 B)        (异地/介质 B)

关键原则:

  1. 备份必须自动化,脚本跑在 cron / k8s CronJob 里,跑完发指标到监控;
  2. 备份产物要校验完整性(pg_verifybackup 校验 basebackup),别等恢复时才发现文件损坏;
  3. WAL 归档和全量备份必须分目录/分 bucket,防止全量备份覆盖掉归档;
  4. 加密:静态加密 + 传输加密,备份里是全量数据,泄露即等同于数据库泄露;
  5. 测试恢复,这一步见下一节,比备份本身还重要。

保留策略:遵循 GFS(Grandfather-Father-Son)——每日备份保留 7 天,每周备份保留 4 周,每月备份保留 12 个月。既控制成本,又能应对"上周发现的误删"这种延迟发现的事故。


六、恢复演练:没演练过的备份,等于没有

这一节字数最少,却最重要。我见过 90% 的"备份失效"事故,都是因为从来没真正恢复过。

常见翻车场景:

  • 备份脚本天天跑,但目标盘满了,cp 静默失败,没人发现;
  • WAL 归档链有缺口(某段时间 archive_command 报错了),PITR 卡住;
  • 备份加密了,但密钥存在同一台挂掉的机器上;
  • 恢复流程文档过时,真实命令和新版本不兼容;
  • 团队没人会做 PITR,真出事时手忙脚乱,RTO 拉到几小时。

对策:每月一次恢复演练,把它当 生产事故 来对待。

演练清单:

  1. 定期测恢复:在隔离环境,从最新备份拉起一个完整 PG,跑业务只读校验(行数、关键表 checksum、抽样比对);
  2. 测 PITR:挑一个过去的时间点,看能不能精确恢复到那一秒;
  3. 测 failover:主库 kill -9,看 Patroni/repmgr 几秒切到从库,应用连接池是否自动重连;
  4. 记录指标:每次演练记下 RTO(恢复耗时,目标比如 15 分钟)和 RPO(丢数据量,目标比如 0 或秒级);
  5. 复盘文档:演练里踩的坑写进 runbook,下次真出事照着做。

把这套做成 CI 流水线的一部分更好——每次备份成功后自动在临时环境跑恢复校验,失败立刻告警。备份是承诺,恢复是兑现,只有兑现过的承诺才算数。


小结

  1. 复制和备份是两件事:复制保"在线",备份保"可回滚",HA 不能替代 DR。
  2. 流复制是物理复制的底座:WAL 流到从库,近乎实时,但要靠 Patroni 这类工具托管 failover,别手撸。
  3. 同步 vs 异步按业务选:金融账本走同步保 RPO,日志内容走异步保性能,关键事务还能临时切同步。
  4. 物理备份 + WAL 归档 = PITR:能恢复到任意时间点,这是逻辑备份做不到的,生产必配。
  5. 3-2-1 + 恢复演练:3 份副本、2 种介质、1 份异地;每月测一次真恢复,没演练过的备份等于没有。

金句:"数据库的可靠性,不在于你做了几份备份,而在于你能在多久之内把数据完好地恢复回来——并证明它真的能。" 备份是写下来的承诺,恢复是兑现的那一刻。把每一次演练都当成事故预演,当真正的故障在凌晨四点敲门时,你才有底气说:数据没事,接着睡。

Related

相关推荐