PostgreSQL 架构原理第五期:备份与恢复 —— 物理备份、PITR 与复制技术
引言
在前四期中,我们从进程模型、存储引擎、事务与并发控制,到查询优化器,逐步深入了解了 PostgreSQL 的内核原理。数据是企业的核心资产,如何保证数据不丢失、如何从灾难中快速恢复、如何实现 7×24 小时高可用,是数据库运维的必修课。
本期将聚焦 PostgreSQL 的备份与恢复体系,涵盖以下内容:
- 逻辑备份与物理备份的对比与适用场景
- 基于 WAL 的持续归档与时间点恢复(PITR)原理
pg_basebackup与低级别基础备份的实现机制- 物理复制(流复制)与高可用架构(主备、同步/异步、故障切换)
- 逻辑复制的原理、用途与与物理复制的差异
- 备份恢复的最佳实践与常见陷阱
一、备份方法概览:逻辑备份 vs 物理备份
| 特性 | 逻辑备份 | 物理备份 |
|---|---|---|
| 工具 | pg_dump / pg_dumpall | pg_basebackup、文件系统快照 |
| 备份内容 | SQL 语句或自定义格式的归档 | 数据目录的完整二进制文件(包括 WAL 文件) |
| 恢复粒度 | 可恢复单个表或数据库 | 只能整个集群恢复(不能跨版本或选择性恢复) |
| 恢复速度 | 慢(需要重放 SQL 并重建索引) | 快(直接复制文件,应用 WAL) |
| 并行能力 | 可并行(-j 参数) | 支持并行传输 |
| 适用场景 | 小数据量、跨版本升级、部分数据导出 | 大数据量、灾难恢复、高可用搭建 |
| 一致性保证 | 基于快照(SERIALIZABLE) | 基于 WAL 的全局一致性(需要归档或使用复制协议) |
核心结论:
- 日常开发、小规模迁移用逻辑备份。
- 生产环境中,基于物理备份 + WAL 归档的 PITR 方案是最可靠的容灾手段。
二、物理备份基础:数据目录与 WAL 文件
PostgreSQL 的物理备份本质上是文件系统级别的复制,但必须保证复制出的文件处于一致性状态。单纯用 cp 或 rsync 复制正在运行的数据库的数据目录,会得到不一致的备份(部分已刷盘的页面 vs 部分未刷盘)。为了解决这个问题,PostgreSQL 提供了两种方法:
- 低级别 API:使用
pg_start_backup()与pg_stop_backup()配合文件系统工具。 - 高层工具:
pg_basebackup内部使用复制协议,自动完成上述步骤。
2.1 低级别基础备份流程
-- 1. 开始备份,创建一个标签文件(backup_label)
SELECT pg_start_backup('label_name');
-- 2. 使用外部工具拷贝整个数据目录(可以跳过 pg_wal 目录,但需要另外归档)
cp -R $PGDATA /backup/base
-- 3. 结束备份,自动生成备份历史文件
SELECT pg_stop_backup();
pg_start_backup执行检查点,强制将当前 WAL 位置写到backup_label文件中,并记录起始 LSN。- 拷贝期间数据库可以继续读写,但所有修改都会写入 WAL。
pg_stop_backup生成一个备份历史文件(.backup),其中包含备份的起始/结束 LSN 以及所需 WAL 文件列表。恢复时需要这些 LSN 信息。
这种方法的优点是不依赖 pg_basebackup,适合结合 ZFS、LVM 快照或 rsync 脚本实现定制化备份。
2.2 pg_basebackup 工具
pg_basebackup 使用复制协议连接到 PostgreSQL 主库,自动完成 pg_start_backup、数据传输和 pg_stop_backup。常用选项:
pg_basebackup -D /path/to/backup -F t -z -X fetch -P -U repluser -h host
-F t:输出为 tar 包;-F p为普通目录。-X fetch:备份过程中同时获取所需的 WAL 段文件(stream模式更实时)。-P显示进度。
注意:pg_basebackup 要求备份用户具备 REPLICATION 权限,并且需要启用 max_wal_senders > 0。
三、持续 WAL 归档与时间点恢复(PITR)
物理备份本身是某一时刻的静默拷贝,要恢复到最新状态或任意时间点,必须配合增量 WAL 日志。
3.1 WAL 归档的原理
在 postgresql.conf 中设置:
archive_mode = on
archive_command = 'cp %p /archive/%f' # 将完成的 WAL 段文件复制到归档目录
每当 WAL 段文件被写满(默认 16MB)切换时,PostgreSQL 会执行 archive_command 将文件保存到永久归档位置。归档完成后才能重复使用该段文件。
恢复时,PostgreSQL 会从基础备份开始,按顺序重放归档中的 WAL 段文件,前滚到指定时间点。
3.2 时间点恢复(PITR)步骤
- 准备基础备份:将备份文件拷贝到数据目录。
- 配置恢复参数:在数据目录创建
recovery.signal(PostgreSQL 12+)和postgresql.auto.conf或recovery.conf(旧版本)。restore_command = 'cp /archive/%f %p' recovery_target_time = '2025-03-15 14:30:00' recovery_target_action = 'promote' - 启动数据库:实例进入恢复模式,自动应用 WAL 直到指定的目标时间、事务 ID 或 LSN。
- 提升为主库:到达目标点后,数据库停止应用并接受连接(若设置了
promote)。
恢复时还可以使用 recovery_target_xid、recovery_target_lsn 或 recovery_target_inclusive 控制边界。
3.3 归档的注意事项
- 归档命令必须返回零退出码表示成功。建议在命令中加入
rsync、scp或上传到对象存储(如 S3)。 - 确保归档目录的磁盘空间充足,定期清理过旧的归档(通过
pg_archivecleanup或recovery_end_command)。 - 如果 WAL 归档延迟或丢失,将无法完全恢复。可以使用同步备库配合
synchronous_commit增强数据可靠性。
四、流复制:从异步到同步的高可用
流复制(Streaming Replication)是 PostgreSQL 最常用的高可用方案。备库通过 WAL 接收进程实时获取主库生成的 WAL 记录,并立即重放到备库中,实现接近实时的数据同步。
4.1 流复制架构
- 主库:
wal_level = replica或logical,max_wal_senders至少为 1,配置primary_conninfo在备库中指向主库。 - 备库:从基础备份恢复,配置
primary_conninfo,创建standby.signal文件(PostgreSQL 12+)或设置standby_mode = on。 - 复制槽:可选,用于防止主库在备库未接收 WAL 时移除仍需要的 WAL 段。
流复制的副本可以是只读的,接受查询(称为热备),但不能执行写操作。可以设置多个备库实现负载分摊。
4.2 同步与异步复制
- 异步复制:主库提交事务后,无需等待备库确认即返回客户端。性能好,但故障时可能丢失少量数据。
- 同步复制:需等待至少一个同步备库写入磁盘(由
synchronous_standby_names指定),返回客户端前确保数据已在备库持久化。
synchronous_commit = on
synchronous_standby_names = 'FIRST 1 (standby1)' # 等待第一个同步备库
同步复制能保证零数据丢失(RPO=0),但会引入延迟和写吞吐量下降。
4.3 流复制内部机制
主库上的 WAL 发送进程(walsender)不断从 WAL 缓冲区或磁盘上读取 WAL 记录,发送给备库的 walreceiver 进程。备库收到后写入本地 WAL,再由启动进程重放到数据文件。
关键参数:
wal_keep_size(旧版wal_keep_segments):主库保留的 WAL 段数量,避免备库落后太多时 WAL 被覆盖。max_slot_wal_keep_size:为复制槽额外保留的 WAL 上限。hot_standby:备库是否接受只读查询。
4.4 故障切换与灾难恢复
当主库故障时,可以手工或通过集群管理工具(如 Patroni、repmgr)将一个备库提升为新主库。提升操作:
-- 在备库执行
pg_ctl promote -- 或 SELECT pg_promote();
- 旧主库若恢复,可作为新备库重新加入(需重新设置
primary_conninfo并重建)。 - 为避免脑裂,通常需要配合仲裁机制(etcd、ZooKeeper)或使用 Pacemaker。
五、逻辑复制:基于表级的数据分发
与物理复制复制整个数据库集群不同,逻辑复制允许选择性地复制特定表,甚至在不同大版本之间复制数据(常用于升级)。
5.1 核心概念
- 发布者(Publisher):定义哪些表的哪些变更需要发布(可细分为 INSERT、UPDATE、DELETE、TRUNCATE)。
- 订阅者(Subscriber):订阅一个或多发布者,接收变更并应用。
- 复制标识(REPLICA IDENTITY):每个表必须设置,用于标识 UPDATE/DELETE 时的行(默认主键或
FULL)。
-- 发布者
CREATE PUBLICATION mypub FOR TABLE t1, t2;
-- 订阅者
CREATE SUBSCRIPTION mysub CONNECTION 'host=pub_host dbname=test' PUBLICATION mypub;
5.2 逻辑复制与物理复制的区别
| 特性 | 物理复制 | 逻辑复制 |
|---|---|---|
| 复制粒度 | 整个实例 | 表级、行级选择性复制 |
| 版本兼容性 | 必须相同主版本(可能需要小版本一致) | 不同大版本(如 14 到 16)可以复制 |
| DDL 复制 | 自动复制所有 DDL | 不复制 DDL(需手动在订阅端执行) |
| 冲突处理 | 无冲突(物理一致) | 可能出现冲突(唯一约束、重复行等),需要用户处理 |
| 数据筛选 | 否 | 可以指定 WHERE 条件(仅部分行) |
| 双向复制(多主) | 不支持 | 可借助扩展(如 pglogical)实现有限的双向复制 |
| 初始同步 | pg_basebackup 全量拷贝 | CREATE SUBSCRIPTION 时自动拷贝现有数据 |
逻辑复制的典型应用:数据聚合到数据仓库、跨版本在线升级、多主架构(需谨慎设计冲突解决)。
5.3 逻辑复制的内部流程
- 发布端通过
walsender进程读取 WAL,利用解码插件(pgoutput、test_decoding)将 WAL 转换为逻辑变更消息。 - 订阅端
apply进程接收消息,并在本地执行 SQL 以重放变更。 - 工作过程基于 LSN 跟踪,保证精确一次交付(at-least-once)和断点续传。
六、备份恢复策略与最佳实践
6.1 制定 RPO 与 RTO
- RPO(数据恢复点目标):可容忍丢失多少数据?零丢失需同步复制或 WAL 归档无延迟。
- RTO(恢复时间目标):多久恢复业务?物理备份 + PITR 比逻辑备份恢复更快;流复制备库可实现秒级切换。
6.2 备份分层方案
- 全量物理备份(每周一次,例如周日凌晨)。
- 持续 WAL 归档(实时或每分钟执行
archive_command或使用pg_receivewal)。 - 逻辑备份(每天一次用于应急或误删除数据恢复)。
- 归档到异地(对象存储或远程 NFS),防止单机房灾难。
6.3 恢复演练
- 定期在测试环境执行完整的 PITR 恢复,验证归档完整性。
- 演练后检查
pg_wal目录下的 .history 文件,可跟踪时间线分支(timeline),防止恢复后与旧主库通信混乱。
6.4 常见陷阱与注意事项
- 事务 ID 回卷:备份中的数据库长时间未运行,再次恢复时可能因
pg_control中的xid远小于当前而被视为过期。需要在备份前确保VACUUM FREEZE已经执行。 - 归档命令失败:导致 WAL 堆积在
pg_wal,耗尽磁盘。监控pg_stat_archiver视图,并设置archive_timeout强制切换。 - 备库延迟过大:复制槽会阻止主库清理 WAL,如备库长期故障,主库磁盘可能爆满。设置
max_slot_wal_keep_size或监控备库落后字节数。 - 误操作恢复:逻辑备份可以提取单个表;物理备份 + PITR 需要恢复到误操作时间点前。建议保留较长时间的逻辑备份和归档。
七、总结与预告
本期详细介绍了 PostgreSQL 的备份与恢复体系:
- 逻辑备份与物理备份的选型与原理。
- 基础备份与 WAL 归档构成 PITR 的核心。
- 流复制(物理复制)实现了高可用和实时容灾,支持同步/异步模式。
- 逻辑复制提供了更灵活的发布订阅模型,适合数据分发和跨版本升级。
- 最佳实践应包括定期恢复演练、异地归档和监控归档/复制状态。
理解这些机制,我们不仅能防范数据丢失,还能设计出满足业务 RPO/RTO 的高可用架构。
第六期将是本次系列的最后一期,我们将探讨 PostgreSQL 性能调优实战,结合前五期的知识,从硬件配置、参数调优、SQL 优化、监控诊断等方面给出系统性的方法论和案例。
思考题
- 为什么不建议在生产环境直接使用
pg_dump作为唯一的备份手段?物理备份相比它有哪些不可替代的优势? - 如果主库磁盘损坏导致 WAL 归档中断,而备库还保持同步,如何恢复主库?切换后如何防止旧主库的错误回切?
- 逻辑复制能否完全替代流复制来实现高可用?为什么?