PostgreSQL的流复制(Streaming Replication)
PostgreSQL 的流复制(Streaming Replication)是 PostgreSQL 原生提供的一种物理复制技术。它是目前 PostgreSQL 实现高可用性(HA)、读写分离和灾难恢复的最核心、最常用的手段。
自 PostgreSQL 9.0 引入以来,流复制经过不断完善,已经非常稳定和高效。
以下是关于 PostgreSQL 流复制的全面解析:
1. 流复制的核心原理
流复制是基于 WAL(Write-Ahead Log,预写式日志) 实现的。它的本质是把主库(Primary)上产生的底层物理数据变更日志(WAL),源源不断地“流”向备库(Standby),备库接收到后重放(Replay)这些日志,从而保证主备数据一致。
工作流程及核心进程:
- 主库(Primary): 产生 WAL 日志。主库上会启动一个
walsender进程。 - 备库(Standby): 启动一个
walreceiver进程。它连接到主库的walsender,请求获取 WAL 日志。 - 网络传输:
walsender通过 TCP 连接将 WAL 数据流式传输给walreceiver。 - 备库落盘与重放:
walreceiver将接收到的 WAL 写入备库的磁盘,然后备库的startup(启动进程)读取这些 WAL 并应用到备库的数据文件中。
(注意:流复制是“物理复制”,即主备库在磁盘上的数据块(Block)是完全一模一样的。备库严格来说是一个处于“持续恢复状态”的只读副本。)
2. 复制模式:异步 vs 同步
流复制支持两种模式:
A. 异步复制(Asynchronous Replication)- 默认
- 原理: 主库执行事务,将 WAL 写入本地磁盘后,直接向客户端返回“提交成功”,不等待备库接收 WAL。
- 优点: 性能极高,主库的写入速度不受网络和备库性能的影响。
- 缺点: 如果主库突然宕机,部分还未传输到备库的 WAL 会丢失,导致主备切换时丢失极少量数据。
B. 同步复制(Synchronous Replication)
- 原理: 主库执行事务,将 WAL 写入本地后,必须等待至少一个同步备库确认已经接收并写入了 WAL,主库才会向客户端返回“提交成功”。
- 优点: 保证零数据丢失(RPO = 0)。
- 缺点: 写性能下降。主库的提交延迟取决于网络延迟和备库的 I/O 速度;如果同步备库宕机,主库的写操作会被阻塞(可以配置多个备库或超时策略来缓解)。
3. 如何搭建流复制(以 PostgreSQL 12+ 为例)
注意:PostgreSQL 12 废弃了 recovery.conf 文件,相关配置合并到了 postgresql.conf,并使用 standby.signal 标识备库。
假设:主库 IP 192.168.1.100,备库 IP 192.168.1.101。
第一步:配置主库 (Primary)
- 创建复制专属用户:sql
CREATE ROLE repl_user WITH REPLICATION LOGIN PASSWORD 'YourPassword'; - 修改
pg_hba.conf允许备库连接:plaintext# TYPE DATABASE USER ADDRESS METHOD host replication repl_user 192.168.1.101/32 scram-sha-256 - 修改
postgresql.conf参数:plaintextlisten_addresses = '*' # 监听所有IP wal_level = replica # 必须是 replica 或 logical max_wal_senders = 10 # 允许的最大 walsender 进程数 wal_keep_size = 1GB # (PG13+) 保留的WAL大小,防止备库断开太久WAL被清理 (PG12及以前叫 wal_keep_segments) - 重启主库:
systemctl restart postgresql
第二步:配置备库 (Standby)
- 停止备库服务,并清空备库原有的数据目录(非常重要):bash
systemctl stop postgresql rm -rf /var/lib/pgsql/data/* - 使用
pg_basebackup从主库克隆数据:参数解释:bashpg_basebackup -h 192.168.1.100 -U repl_user -D /var/lib/pgsql/data -Fp -Xs -P -R-h主库IP,-U复制用户,-D目标数据目录。-Fp纯文本格式输出。-Xs在备份过程中同时流式获取 WAL(保证备份的一致性)。-P显示进度。-R(最关键):自动生成standby.signal文件,并在postgresql.auto.conf中自动写入primary_conninfo(连接主库的配置)。
- 确保备库目录权限正确,启动备库:bash
chown -R postgres:postgres /var/lib/pgsql/data systemctl start postgresql
第三步:验证状态
在主库执行:
SELECT * FROM pg_stat_replication;
如果能看到一条记录,且 state 为 streaming,说明流复制已经建立并正常运行。
在备库执行:
SELECT * FROM pg_stat_wal_receiver;
4. 主备切换与故障转移(Failover / Switchover)
由于流复制中备库是只读的,当主库宕机时,需要将备库提升(Promote) 为新的主库。
手动提升备库:
在备库服务器上执行:
pg_ctl promote -D /var/lib/pgsql/data
或者在备库数据库内执行 SQL:
SELECT pg_promote();
提升后,备库会解除只读状态,变为可读写的主库。
自动高可用(HA):
生产环境中,通常不会手动去 Promote 数据库。业界有成熟的第三方组件来监控主备状态,并在主库故障时自动进行切换(并管理虚拟 IP):
- Patroni(目前最主流、最推荐的方案,依赖 etcd/Consul/ZooKeeper)
- Repmgr
- Pacemaker + Corosync
- Keepalived (较老派,容易出现脑裂)
5. 常见问题与优化策略
- 主备延迟(Replication Lag):
- 原因:网络带宽不足、备库 I/O 性能差、主库有大量写事务。
- 监控:主库上的
pg_stat_replication视图可以查看发送和重放的 LSN 差距。
- 流复制中断(WAL 被覆盖):
- 如果备库断开连接太久,主库上旧的 WAL 被循环覆盖清理了,备库重连后会报错找不到 WAL。
- 解决:
- 配置合理的
wal_keep_size。 - 推荐方案: 使用 Replication Slots(复制槽)。主库会为存在复制槽的备库强制保留 WAL,直到备库消费它们。
- 配置合理的
- 读写分离与查询冲突:
- 备库提供只读查询。但如果备库上一个耗时很长的查询锁定了某些数据,而主库刚好发来了删除/更新该数据的 WAL 操作,就会发生“复制冲突”。
- 解决: 配置参数
max_standby_streaming_delay(允许备库延迟重放 WAL 的时间)或hot_standby_feedback = on(备库告诉主库不要清理备库正在查询的死元组)。
6. 流复制 vs 逻辑复制 (Logical Replication)
| 特性 | 物理流复制 (Streaming Replication) | 逻辑复制 (Logical Replication) |
|---|---|---|
| 复制粒度 | 整个数据库集群(实例级) | 表级、库级 |
| 底层原理 | 传输物理块的 WAL | 解析 WAL 为逻辑 SQL (Insert/Update) |
| 版本要求 | 主备大版本必须一致 | 支持跨版本(如 PG 11 复制到 PG 15) |
| 备库状态 | 严格只读 | 目标端依然可读写 |
| 主要用途 | 高可用(HA)、灾备、全库读写分离 | 数据同步、汇总、跨版本升级、特定表分发 |