☰
PostgreSQL流复制高可用架构实战:从原理到生产环境配置与故障切换
2026/10/22 11:17:42 网站建设 项目流程

1. 为什么流复制是 PostgreSQL 高可用架构的基石

第一次接触 PostgreSQL 主从同步的人,往往会被一堆名词绕晕:物理复制、逻辑复制、流复制、归档恢复、同步提交、异步提交……其实把场景想清楚就简单了。假设你手头有一套业务系统,数据库单点部署,某天凌晨磁盘突然挂了,或者机房网络抖了一下,业务直接停摆。这时候你需要的不是“备份”,而是“另一台随时能顶上的数据库”。流复制解决的正是这个问题:主库写数据的同时,把预写日志(WAL)实时推送到从库,从库持续重放,最终达到和主库几乎一致的状态。

我最早在生产环境上流复制是在一套订单系统上,当时主库压力不算大,但业务方对数据丢失的容忍度极低,要求故障切换后最多丢几秒的数据。这个需求直接决定了后面所有参数的选择方向。所以你在动手之前,先问自己三个问题:能接受多少数据丢失(RPO)?能接受多长停机时间(RTO)?从库是只做备份,还是要承担读流量?这三个答案会贯穿整篇文章的每一个配置决策。

流复制的核心机制其实不复杂。PostgreSQL 把所有数据变更先写进 WAL 日志,主库有一个 walsender 进程负责把 WAL 发给从库,从库有一个 walreceiver 进程接收并写入本地,再由 startup 进程重放。整个过程是“日志级”的复制,不是 SQL 级,所以从库拿到的是块级别的物理变更,效率高、延迟低,这也是它比逻辑复制更适合做整库热备的原因。逻辑复制虽然灵活,可以按表订阅,但延迟和一致性保障上不如物理流复制来得直接。

适合读这篇文章的人,我大致分三类:一是刚接手 PostgreSQL 运维、需要搭一套主从的工程师;二是已经在用但延迟忽高忽低、想搞清楚参数怎么调的人;三是准备做故障切换演练、需要一套可复现方案的架构同学。不管你是哪一类,下面的内容都会从原理讲到实操,再讲到踩坑,尽量让你看完就能上手。

2. 主从架构设计与复制模式选型

2.1 物理流复制和逻辑复制的取舍逻辑

很多人一上来就问“用哪个好”,其实这个问题没有标准答案,得看你要解决什么。物理流复制复制的是整个集群的物理块变更,从库是一个完整的、只读的副本,适合做灾难恢复、读写分离的只读节点。逻辑复制复制的是行级变更,可以跨版本、跨平台,适合做数据汇聚、部分表同步、灰度升级。两者不是替代关系,而是互补。

我个人的经验是:如果目标是“整库热备 + 快速切换”,物理流复制是首选,因为它简单、延迟低、切换后数据完整。如果目标是“把几张核心表同步到另一个业务库做分析”,逻辑复制更合适。本文聚焦物理流复制,因为它是绝大多数生产环境高可用方案的地基。

2.2 同步提交与异步提交的权衡

这是流复制里最关键的决策点,没有之一。异步提交下,主库提交事务后立刻返回成功,WAL 异步发给从库,如果主库在发送前崩溃,这部分数据就丢了。同步提交下,主库要等从库确认收到 WAL 才返回成功,数据安全性高,但主库的写入延迟会受从库影响。

这里有个参数叫synchronous_commit,取值有on、remote_apply、remote_write、local、off。我一般这么选:

取值含义数据安全性写入延迟适用场景
off不等本地 WAL 落盘最低最低对丢失不敏感
local等本地 WAL 落盘中低单机可靠性
remote_write等从库写入 OS 缓存较高中多数生产环境
on等从库 WAL 落盘高较高金融级要求
remote_apply等从库重放完成最高最高强一致读

实测下来,remote_write是性价比最高的选择,既保证了从库收到了数据,又不会因为从库重放慢而拖累主库。如果你的业务能接受秒级数据丢失,异步提交(local或off)配合监控告警也够用。但要注意,同步提交如果配置了多个同步备库,任何一个卡住都会让主库写入阻塞,所以synchronous_standby_names的写法很讲究,后面会细说。

2.3 一主一从还是一主多从

一主一从结构简单,适合中小规模。一主多从可以分担读流量,但同步提交模式下,主库要等所有列在synchronous_standby_names里的从库确认,任何一个从库网络抖动都会影响主库。所以我的建议是:同步备库只选一个最稳定的节点,其他从库用异步,这样既保证了核心数据安全,又不会因为某个从库拖垮整个集群。

级联复制(从库再往下挂从库)也是常见做法,能减轻主库的发送压力。但级联层数越多,延迟越大,故障排查也越复杂。我一般控制在两层以内,再深就不如直接多挂几个一级从库了。

3. 环境准备与主库核心配置实操

3.1 基础环境与版本一致性检查

动手之前,先把两台机器的 PostgreSQL 版本对齐。主从版本不一致,物理流复制基本跑不起来,因为 WAL 格式可能不兼容。用SELECT version();确认两边大版本号一致,小版本尽量也一致。操作系统层面,建议用相同的发行版和架构,避免字节序、文件路径差异带来的问题。

网络方面,主从之间要能互通 5432 端口(或者你自定义的端口)。我习惯在配置前先用telnet或nc测一下连通性,别等到配置完了才发现防火墙没开。时间同步也要做,NTP 对时能保证日志时间戳可比对,排查延迟问题时特别有用。

3.2 postgresql.conf 关键参数逐项拆解

主库的postgresql.conf是流复制的核心,下面这几个参数必须改:

# 监听地址,允许从库连接 listen_addresses = '*' # 开启 WAL 归档级别,流复制至少需要 replica wal_level = replica # 允许的最大 WAL 发送进程数,至少留一个给从库 max_wal_senders = 10 # 保留的 WAL 段数量,防止从库断连后 WAL 被删 wal_keep_size = 1GB # 同步备库列表,先留空,后面按需配置 synchronous_standby_names = '' # 开启 WAL 归档(可选,但强烈建议) archive_mode = on archive_command = 'cp %p /var/lib/postgresql/archive/%f'

wal_level从minimal改成replica需要重启数据库,这个不能省。max_wal_senders我一般设成从库数量加 5,留点余量给临时连接。wal_keep_size是个容易踩坑的参数,设太小的话,从库一旦断连超过这个量,主库就把 WAL 删了,从库再连上就得重新做基础备份。1GB 是个保守值,WAL 写入量大的系统可以调到 5GB 甚至 10GB。

archive_command里的路径要提前建好,权限给 postgres 用户。归档不是流复制的必需项,但它能在从库彻底追不上时救你一命,配合基础备份可以做时间点恢复。

3.3 pg_hba.conf 复制权限配置

从库要连主库拉 WAL,必须在pg_hba.conf里放行。加一行:

# 允许从库 IP 以 replication 身份连接 host replication repl_user 192.168.1.0/24 scram-sha-256

这里我专门建了一个repl_user账号,而不是用超级用户。复制账号只需要REPLICATION权限,最小权限原则能降低风险。创建命令:

CREATE ROLE repl_user WITH REPLICATION LOGIN PASSWORD 'your_strong_password';

密码认证方式建议用scram-sha-256,比md5安全。改完pg_hba.conf记得reload,不用重启。

3.4 基础备份:pg_basebackup 的正确用法

从库的初始数据必须从主库来,最标准的方式是pg_basebackup。在主库或从库上执行都行,我习惯在从库上执行,直接拉到本地:

pg_basebackup -h 192.168.1.10 -p 5432 -U repl_user \ -D /var/lib/postgresql/14/main \ -Fp -Xs -P -R

参数逐个解释:-h主库地址,-D从库数据目录,-Fp表示普通文件格式(不是 tar),-Xs表示用流复制方式传 WAL,-P显示进度,-R自动生成standby.signal和postgresql.auto.conf里的primary_conninfo。这个-R特别省事,省得你手动写连接信息。

注意:执行pg_basebackup前,从库的数据目录必须是空的,否则会报错。如果之前有数据,先清空再执行。

备份完成后,从库数据目录下会多一个standby.signal文件,这个文件的存在告诉 PostgreSQL“我是一个从库,启动后进入恢复模式”。没有它,从库会以主库身份启动,那就乱套了。

4. 从库配置与复制链路验证

4.1 standby.signal 与 primary_conninfo 配置

pg_basebackup -R已经帮你生成了postgresql.auto.conf里的连接串,内容大概是这样:

primary_conninfo = 'host=192.168.1.10 port=5432 user=repl_user password=your_strong_password'

如果你没加-R,就得手动创建standby.signal(空文件即可)并在postgresql.auto.conf里写上面这行。primary_conninfo里的密码是明文,所以文件权限要控制好,chmod 600是基本操作。

从库的postgresql.conf里还有几个参数值得关注:

# 从库必须开启热备,否则无法接受只读查询 hot_standby = on # 从库反馈给主库的延迟信息级别 wal_receiver_status_interval = 10s # 从库重放冲突时的处理策略 hot_standby_feedback = on

hot_standby_feedback = on这个参数争议比较大。开启后,从库会把正在执行的查询所需的最老快照告诉主库,主库就不会清理这些行,避免从库查询被取消。但代价是主库的垃圾回收会被拖慢,表膨胀风险增加。我的建议是:如果从库只做备份、不跑长查询,关掉它;如果从库要承担读流量且有长查询,开启它,但要监控主库的膨胀情况。

4.2 启动从库并确认复制状态

从库配置好后,直接启动:

pg_ctl start -D /var/lib/postgresql/14/main

启动后,在从库执行SELECT pg_is_in_recovery();,返回t说明处于恢复模式,也就是从库身份正常。然后在主库执行:

SELECT client_addr, state, sync_state, sent_lsn, replay_lsn, pg_wal_lsn_diff(sent_lsn, replay_lsn) AS replay_lag_bytes FROM pg_stat_replication;

这条查询是流复制运维的“仪表盘”。state应该是streaming,sync_state显示async或sync,replay_lag_bytes是从库还没重放的字节数。如果这个值持续增长,说明从库追不上主库,得查原因。

在从库上也可以查延迟:

SELECT now() - pg_last_xact_replay_timestamp() AS replay_delay;

这个返回的是从库最后重放事务的时间戳与当前时间的差,单位是时间。如果主库没有新写入,这个值会一直增大,所以它只适合在主库有持续写入时参考。

4.3 同步复制的启用与 synchronous_standby_names 写法

确认异步复制跑通后,如果业务需要同步复制,就在主库改:

synchronous_standby_names = 'FIRST 1 (standby1, standby2)'

这个写法的含义是:在standby1和standby2中,只要有一个确认收到 WAL,主库就返回成功。FIRST 1表示按列表顺序取第一个可用的。还有ANY 1 (...)的写法,语义类似但优先级规则不同。我一般用FIRST 1,把最稳定的节点放前面。

改完synchronous_standby_names后reload即可生效。然后在主库查pg_stat_replication,对应的从库sync_state会变成sync。这时候你可以在主库做个写入测试,观察从库是否同步收到。

注意:同步复制下,如果所有同步备库都挂了,主库的写入会阻塞。所以生产环境一定要配置synchronous_commit = remote_write而不是on,并且至少保留一个异步备库作为兜底。

5. 延迟监控、故障切换与常见问题排查

5.1 复制延迟的量化监控方法

延迟分两种:发送延迟和重放延迟。发送延迟是主库发了但从库没收到,通常是网络问题;重放延迟是从库收到了但没重放完,通常是从库 IO 或 CPU 瓶颈。用pg_stat_replication里的sent_lsn、write_lsn、flush_lsn、replay_lsn四个值可以精确定位卡在哪一环。

我习惯写一个简单的监控脚本,每分钟采集一次:

SELECT application_name, pg_wal_lsn_diff(pg_current_wal_lsn(), sent_lsn) AS send_lag, pg_wal_lsn_diff(sent_lsn, replay_lsn) AS replay_lag FROM pg_stat_replication;

send_lag持续大于 0 且增长,查网络和主库 walsender 进程;replay_lag持续增长,查从库的磁盘 IO 和max_wal_size设置。从库重放是单进程的,如果主库写入并发很高,从库重放可能成为瓶颈,这时候可以考虑开启recovery_parallel_workers(PostgreSQL 15 及以上支持并行重放)。

5.2 主库故障后的从库提升操作

主库挂了,要把从库提成新主库,操作很简单:

pg_ctl promote -D /var/lib/postgresql/14/main

或者在从库执行SELECT pg_promote();。提升后,从库的standby.signal会被删除,pg_is_in_recovery()返回f,它就成了一个可写的主库。这时候你要做的第一件事是检查数据一致性,确认最后重放到的 LSN,评估丢了多少数据。

如果原主库还能救回来,把它作为新主库的从库重新接入,需要重新做pg_basebackup,因为原主库的 WAL 分叉了,不能直接续上。这是流复制的一个硬限制,也是为什么我反复强调wal_keep_size和归档的重要性。

5.3 常见问题速查表

现象可能原因排查方向解决方式
从库 state 一直 startup连接信息错误查从库日志 primary_conninfo修正连接串,重启从库
replay_lag 持续增长从库 IO 瓶颈查从库磁盘 IO、CPU升级磁盘,开并行重放
主库写入阻塞同步备库全挂查 pg_stat_replication临时改异步,恢复备库
从库查询被取消主库清理了旧行查主库 vacuum 日志开 hot_standby_feedback
复制槽导致 WAL 堆积从库长期断连查 pg_replication_slots删无用槽,加监控
提升后无法写入未真正 promote查 pg_is_in_recovery确认 promote 成功

复制槽是个双刃剑。它能防止主库删除从库还没收到的 WAL,但如果从库长期不连,WAL 会无限堆积,撑爆磁盘。我一般会为每个从库建一个物理复制槽,同时加磁盘告警,超过阈值就人工介入。

5.4 我踩过的几个坑

第一个坑是wal_keep_size设太小。有次从库网络抖了半小时,恢复后直接报“requested WAL segment has already been removed”,只能重做基础备份。后来我把这个值调到 5GB,再没出过类似问题。

第二个坑是同步复制下从库磁盘满了。从库写不进去,主库等确认等到超时,整个集群写入卡死。教训是:同步备库的磁盘监控要比主库还严,因为它的故障会直接传导到主库。

第三个坑是pg_basebackup时没加-Xs,WAL 传输用了fetch模式,结果备份期间主库 WAL 被清理,备份出来的数据不完整。-Xs用流复制传 WAL,和备份并行,基本不会出现这个问题。

6. 生产环境加固与扩展思路

6.1 复制槽与 WAL 保留策略

生产环境我建议每个从库配一个物理复制槽:

SELECT pg_create_physical_replication_slot('standby1_slot');

然后在从库的primary_conninfo里加slot_name=standby1_slot。这样主库就知道这个从库还没消费到哪,不会提前删 WAL。但复制槽的监控必须做,pg_replication_slots里的restart_lsn和当前 LSN 的差距就是堆积量,超过阈值告警。

6.2 读写分离场景下的从库调优

如果从库要承担读流量,hot_standby_feedback和max_standby_streaming_delay要配合调。前者防止查询被取消,后者控制从库重放等待查询的最长时间。我一般设max_standby_streaming_delay = 30s,超过这个时间,从库会取消冲突查询继续重放,保证延迟不失控。

从库的shared_buffers和work_mem可以按读业务特点调大,因为从库不写数据,内存更多用于查询缓存。但要注意,从库重放也会用共享缓冲区,别把内存全给查询了。

6.3 多级级联与跨机房复制的注意点

跨机房复制延迟天然比同机房高,同步复制基本不可行,只能用异步。这时候synchronous_commit设local或off,配合应用层的补偿逻辑。级联复制能减少主库的发送压力,但中间节点故障会影响下游所有节点,所以中间节点最好也配一个备选路径。

跨机房还要注意带宽和 WAL 生成速率的匹配。如果主库每秒生成 50MB WAL,跨机房带宽只有 100Mbps(约 12.5MB/s),那从库永远追不上。这种场景下要么限流,要么只同步核心表(改用逻辑复制)。

6.4 定期演练与自动化检查清单

流复制搭好只是开始,定期演练才是保命的关键。我一般每季度做一次切换演练:手动 promote 从库,验证业务能连上新主库,再把原主库重新接入。演练前做好备份,演练后核对数据一致性。

日常检查我列了个清单,贴在运维手册里:

  • pg_stat_replication里所有从库 state 是否为 streaming
  • replay_lag是否在可接受范围内
  • 复制槽是否有堆积
  • 主库pg_wal目录大小是否正常
  • 从库磁盘剩余空间是否充足
  • 归档是否正常执行

这套东西跑顺了,流复制基本不会出大问题。真出问题的时候,日志和监控数据就是你的救命稻草,所以别省那点监控成本。

我个人在实际操作中的体会是,流复制的难点不在搭建,而在长期运维中的细节把控。参数调优没有一劳永逸的答案,得根据业务写入模式、网络质量、硬件配置不断调整。先把异步复制跑稳,再逐步上同步,最后补全监控和演练,这个节奏最稳妥。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询