PostgreSQL 流复制(Streaming Replication)是构建读写分离、异地容灾和高可用架构的基石。本文从零演示一主一从的搭建、同步/异步差异、延迟监控与故障切换,所有命令均可直接复制。
一、什么是流复制(为什么需要它)
流复制的原理是:主库把 WAL(Write-Ahead Log,预写日志) 通过 TCP 实时推送到从库,从库持续重放这些日志,从而保持与主库几乎一致的数据状态。它的典型用途有三类:一是 读写分离,把大量只读查询(报表、搜索)分流到从库;二是 容灾,异地从库在主库机房故障时顶上;三是 高可用切换,从库 promoted 成新主库,业务无感知。
关于 PostgreSQL 的体系与日常运维,可参考 PostgreSQL 实战 一文打底。
二、异步 vs 同步:先定 RPO/RTO
搭建前先回答一个问题:能接受丢多少数据?这决定了用异步还是同步复制。
| 维度 | 异步复制 | 同步复制 |
|---|---|---|
| 提交确认 | 主库提交即返回,不等从库 | 至少一个从库确认落盘才返回 |
| 数据安全性 | 可能丢失最后几秒(RPO>0) | 零丢失(RPO=0) |
| 写性能 | 几乎无影响 | 写延迟增加,从库宕机主库被阻塞 |
| 适用场景 | 读多写少、可容忍极小丢失 | 金融交易、订单等强一致需求 |
绝大多数业务先用异步,等对一致性要求极高时再针对性开启同步。
三、环境准备
准备两台机器(或单机用 Docker 跑两个容器演示):主库 192.168.1.10,从库 192.168.1.11,PostgreSQL 15+(大版本必须一致,否则 WAL 不兼容)。
3.1 主库 postgresql.conf
listen_addresses = '*'
wal_level = replica
max_wal_senders = 10
max_replication_slots = 10
wal_keep_size = 1GB # PG13+ 取代旧的 wal_keep_segments
3.2 pg_hba.conf 放行复制连接
# 仅允许从库 IP 用复制专用账号连接
host replication repl_user 192.168.1.11/32 scram-sha-256
3.3 创建复制用户
CREATE ROLE repl_user WITH REPLICATION PASSWORD 'strong_pwd' LOGIN;
修改配置后重载:SELECT pg_reload_conf(); 或 pg_ctl reload。
四、从库基础备份与拉起
用 pg_basebackup 从主库拉一份一致性备份,并顺便生成复制配置:
pg_basebackup -h 192.168.1.10 -U repl_user -D /var/lib/pgsql/15/data \
-Fp -Xs -P -R -C --slot=replica1
其中 -R 会自动生成 standby.signal 与 primary_conninfo,省去手工配置;--slot=replica1 创建复制槽,防止主库过早清理从库还没接收的 WAL。
4.1 关键配置(postgresql.auto.conf)
primary_conninfo = 'host=192.168.1.10 port=5432 user=repl_user password=strong_pwd'
primary_slot_name = 'replica1'
restore_command = 'cp /archive/%f %p' # 若开启归档用于 PITR
五、做成只读从库并验证
启动从库后,先确认它确实在”恢复模式”(即从库),再在主库查 walsender 状态:
-- 在从库执行,应返回 t
SELECT pg_is_in_recovery();
-- 在主库执行,应能看到从库的连接
SELECT pid, usename, application_name, state, sync_state
FROM pg_stat_replication;
在从库建表会被拒绝(默认只读)。读写分离可在应用层用连接池(如 PgBouncer)把读请求路由到从库;慢查询定位可配合 PostgreSQL 慢查询优化。
六、同步复制:把 RPO 压到 0
在主库的 postgresql.conf 指定同步从库:
synchronous_standby_names = 'replica1'
从库通过 application_name 与这个名字匹配。注意:同步从库一旦宕机,主库写操作会被阻塞。生产建议用 quorum 写多个:synchronous_standby_names = 'ANY 1 (replica1, replica2)',任一存活即可,兼顾安全与可用。
七、延迟监控与告警
从库延迟是核心指标。一条 SQL 即可看出复制滞后多少:
SELECT now() - pg_last_xact_replay_timestamp() AS replication_lag;
| 监控字段(pg_stat_replication) | 含义 | 告警建议 |
|---|---|---|
| write_lag / flush_lag / replay_lag | 从库写/落盘/重放落后主库的时长 | replay_lag > 30s 告警 |
| sent_lsn / replay_lsn | 已发送 / 已重放的日志位置 | 两者差值持续增大需排查 |
| state | 复制流状态(streaming/catchup) | 非 streaming 立即排查 |
八、故障切换:手动与自动
主库宕机时,把从库提升为新主库。手动方式(PG12+ 推荐 SQL):
-- 在从库执行,把它提升为主库
SELECT pg_promote();
手动切换慢且易出错,生产常用自动方案:
| 方案 | 特点 | 适用 |
|---|---|---|
| Patroni | 依赖 etcd/Consul,强一致、自动选主、防脑裂 | 中大型、多节点 |
| repmgr | 轻量、自带 witness 节点、运维简单 | 中小型集群 |
| Keepalived + VIP | 最简单,靠 VIP 漂移 | 单从库、容忍脑裂风险 |
九、常见坑与排障清单
- 大版本不一致:主从 PG 大版本必须相同,否则 WAL 格式不兼容,复制直接失败。
- 复制槽堆积撑爆磁盘:
wal_keep_size配合复制槽会保留 WAL,从库长期离线会导致主库磁盘被占满,需监控pg_replication_slots。 - 从库停太久重建:若 WAL 已被清理,需重新
pg_basebackup全量重建。 - 同步复制脑裂:务必配置多个
synchronous_standby_names或用 quorum,避免单点阻塞。
十、与备份、慢查询协同
流复制是”近实时副本”,不是备份——主库误删一张表,删除操作会立刻同步到从库。因此它必须配合 数据库 PITR 时间点恢复 与定期物理备份,才能在误操作时回滚。容器化部署可参考 Docker 实战 把 PG 跑在编排里,便于一键拉起从库。
十一、复制槽(Replication Slot)深入
复制槽是主库上记录”从库还需要哪些 WAL”的对象。有了它,即使 wal_keep_size 设得很小,主库也不会清理从库尚未接收的日志,从而避免从库因 WAL 缺失而中断复制。查看槽状态:
SELECT slot_name, active, restart_lsn, wal_status
FROM pg_replication_slots;
需要警惕的是:物理槽(physical)一旦对应从库离线且不清理,WAL 会无限堆积、最终撑爆主库磁盘,因此必须配合监控告警。临时槽(temporary)在连接断开时自动删除,适合一次性迁移与升级场景。
十二、级联复制与延迟从库
从库还可以再挂载从库,形成 级联复制,把主库的 WAL 发送压力分摊出去。更实用的技巧是 延迟从库:故意让某个从库落后主库几小时重放,一旦主库发生误删数据,能在延迟窗口内从它抢救回来:
# 在从库 postgresql.conf 中设置,落后主库 4 小时再重放
recovery_min_apply_delay = '4h'
延迟从库配合 数据库 PITR 时间点恢复,构成”副本 + 时间点备份”的双保险,是生产环境误操作的最后一道防线。
十三、上线前检查清单
| 检查项 | 命令 / 配置 | 通过标准 |
|---|---|---|
| 大版本一致 | SELECT version(); | 主从完全相同 |
| 复制在流 | pg_stat_replication.state | 值为 streaming |
| 延迟达标 | replay_lag | < 30s |
| 槽不堆积 | pg_replication_slots.wal_status | 非 ‘lost’ |
| 切换演练 | SELECT pg_promote(); | 从库升主成功且原主不再写入 |
掌握流复制后,你就拥有了读写分离、容灾与高可用的底座。下一步可以叠加连接池、负载均衡与自动选主,把单点 PostgreSQL 升级为真正的高可用数据库服务。




