PostgreSQL 原生提供的流复制(Streaming Replication)是构建高可用集群的核心能力。主库产生 WAL 日志后,备库通过网络实时接收并回放这些日志,从而保持与主库的数据一致。当主库发生故障时,备库可以被提升(promote)为新的主库继续对外服务。本文将从原理、搭建步骤、参数调优和自动故障切换四个方面,完整讲解 PostgreSQL 高可用集群的搭建过程。

一、流复制的工作原理
PostgreSQL 的流复制基于 WAL(Write-Ahead Log,预写式日志)机制。主库在事务提交前会先把变更写入 WAL 日志,wal sender 进程负责把产生的 WAL 日志段实时推送给备库,备库的 wal receiver 进程接收后写入本地 WAL 文件,再由 startup 进程不断回放日志,达到与主库一致的状态。
与早期的基于文件日志传送方式相比,流复制的优势在于延迟极低。传统的日志传送需要等一个 WAL 段(默认 16MB)写满才归档传输,而流复制是记录级别的推送,主库一旦生成了 WAL 记录,备库几乎可以立即收到,通常延迟在毫秒级别。这也是流复制成为生产环境标配的原因。
流复制分为异步和同步两种模式。异步模式下主库提交事务后不等待备库确认,性能好但主库崩溃时可能丢失最后几毫秒的数据;同步模式下主库会等待至少一个备库确认收到 WAL 后才返回提交成功,数据零丢失但写入延迟会增加。生产环境需要根据业务对数据一致性和性能的要求来选择,例如金融类系统通常配置同步复制,而日志类、统计类业务用异步复制即可。
二、主库与备库的搭建步骤
假设我们有两台服务器:主库 192.168.1.10,备库 192.168.1.11,操作系统为 Linux,已安装 PostgreSQL 14。搭建的第一步是修改主库的 postgresql.conf 核心参数,打开这些参数后重启主库使其生效。
# 主库 postgresql.conf 关键参数 listen_addresses = '*' wal_level = replica # 开启 WAL 归档级别,流复制最低要求 replica max_wal_senders = 10 # 最大 WAL 发送进程数 wal_keep_size = 1024 # 保留 1GB WAL,防止备库取不到日志(PG13 之前为 wal_keep_segments) hot_standby = on # 允许备库只读查询(主库上配置,备库继承) synchronous_commit = on # 同步复制需要指定同步备库名称 # synchronous_standby_names = 'FIRST 1 (standby1)'
接着配置主库的 pg_hba.conf,允许备库通过复制账号连接。复制账号建议单独创建,只授予复制权限。
# 创建复制用户 createuser -U postgres --replication --login repluser psql -U postgres -c "ALTER USER repluser PASSWORD 'replpass123';" # pg_hba.conf 添加一行 host replication repluser 192.168.1.11/32 md5
备库端使用 pg_basebackup 从主库拉取基础备份,这一步会完整复制主库数据目录并自动生成 standby.signal 文件(PG12 及以后版本用该文件表示备库身份,替代了早期的 recovery.conf)。
# 在备库服务器执行 pg_basebackup -h 192.168.1.10 -U repluser -D /var/lib/postgresql/14/main \ -Fp -Xs -P -R \ -C -S slot_standby1 # 参数说明: # -Fp 备份为普通文件格式 # -Xs 以流方式同步 WAL # -R 自动写入 standby.signal 和 primary_conninfo # -C -S 创建名为 slot_standby1 的物理复制槽
启动备库后,在主库上执行查询即可验证复制状态。如果 state 列显示 streaming,说明流复制已经正常工作。
-- 主库上查看复制状态 SELECT client_addr, state, sync_state, sent_lsn, write_lsn, flush_lsn FROM pg_stat_replication; -- 备库上查看回放进度,false 表示处于恢复模式即备库 SELECT pg_is_in_recovery(); -- 计算复制延迟(字节) SELECT pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) FROM pg_stat_replication;
三、复制槽与延迟监控
异步复制有一个隐患:如果备库长时间宕机,主库可能因为 wal_keep_size 保留的 WAL 不够用而不得不删除旧日志,导致备库再也无法追上主库,只能重建。物理复制槽(Replication Slot)就是为了解决这个问题,它会保证主库在备库确认接收之前不删除对应的 WAL 日志。
但复制槽也有副作用:如果备库彻底损坏且不再回来,主库会无限堆积 WAL 直至磁盘写满。因此在启用复制槽的同时,建议设置 max_slot_wal_keep_size 参数(PG13 及以上)限制单个复制槽最多保留的 WAL 量,超过上限后主库会删除 WAL 并让该复制槽失效,以此保护主库自身安全。此外,日常运维中要持续监控复制延迟,常用的做法是定时执行 pg_wal_lsn_diff 计算字节差,或者在备库创建心跳表来检测时间层面的延迟。
四、基于 Patroni 的自动故障切换
原生的流复制只解决了数据同步问题,主库故障后仍需人工执行 promote 操作,故障恢复时间得不到保障。生产环境通常引入 Patroni 加 etcd 的组合来实现自动故障切换。Patroni 是一个集群管理组件,它把集群状态(谁是主库、备库列表、复制参数等)存储在 etcd 分布式存储中,通过分布式锁选举主库。
当主库发生故障时,etcd 中的 leader 键过期,其余节点的 Patroni 会发起选举,获得多数票的节点自动执行 promote 成为新主库,其他节点则自动重新指向新主库继续复制,整个过程通常在十几秒内完成,无需人工介入。Patroni 还内置了 watchdog 机制防止脑裂,并且可以在故障切换后自动执行回调脚本重建旧的失败节点。
一个最小化的 Patroni 配置示例如下,每个节点的配置只需修改 name 和连接地址。
scope: pgcluster
namespace: /service/
name: node1 # 每个节点唯一
restapi:
listen: 192.168.1.10:8008
connect_address: 192.168.1.10:8008
etcd:
hosts: 192.168.1.20:2379 # etcd 集群地址
postgresql:
listen: 192.168.1.10:5432
connect_address: 192.168.1.10:5432
data_dir: /var/lib/postgresql/14/main
pgpass: /tmp/pgpass
authentication:
superuser:
username: postgres
password: pgpass123
replication:
username: repluser
password: replpass123
parameters:
wal_level: replica
max_wal_senders: 10
hot_standby: on
tags:
nofailover: false
noloadbalance: false
clonefrom: false
nosync: false配置完成后在各节点启动 Patroni 服务,使用 patronictl list 命令即可查看集群拓扑。应用层建议配合 HAProxy 暴露读写端口,HAProxy 通过 Patroni 的 REST 接口(8008 端口的 master 或 replica 路径)做健康检查,读写请求始终路由到当前主库,只读请求可以分发到备库,实现读写分离与故障切换对应用透明。
五、常见问题与注意事项
搭建过程中有几个高频问题值得注意。第一,防火墙和 pg_hba.conf 必须同时放行,缺一个都会导致 wal sender 连接失败,表现为备库日志中反复出现连接超时。第二,备库的数据目录必须为空且属主为 postgres 用户,否则 pg_basebackup 会直接报错退出。第三,升级 PostgreSQL 大版本后,pg_wal 目录改名为 pg_wal(早期为 pg_xlog),部分监控脚本需要同步调整。
另外要理解 RPO 和 RTO 的关系:异步复制 RPO 不是零,故障时可能丢失最近几百毫秒的提交事务;同步复制虽然 RPO 为零,但备库故障会阻塞主库提交,因此生产上常用 FIRST 1 (node1, node2) 配两个候选同步备库,即所谓的一主一同步一异步的准同步架构,在数据安全与可用性之间取得平衡。完成集群搭建后,务必进行故障演练,手动 kill 主库进程验证自动切换和应用的 reconnect 机制,确保高可用方案真正经得起考验。
PostgreSQL高可用流复制Patroni修改时间:2026-09-02 17:07:21