1. 课程导入与高可用全景
在开始讨论流复制、故障切换、连接路由之前,我们需要先建立一个判断标准:什么样的 PostgreSQL 集群才算“高可用”。很多团队把高可用等同于“主库挂了能从库顶上”,但真正上线后才发现,切换确实发生了,业务却因为数据丢失、连接未重连、VIP 未漂移而中断了十几分钟。问题往往不在复制本身,而在于一开始没有把目标量化清楚。
高可用的核心目标:让服务在故障中仍然可预期
PostgreSQL 高可用的核心目标可以概括为三件事:数据不丢或按约定少丢、服务中断时间可控、故障后状态可判断。这三件事分别对应两个关键指标——RPO(Recovery Point Objective,恢复点目标)和 RTO(Recovery Time Objective,恢复时间目标)。
RPO 描述的是“允许丢多少数据”,通常用时间衡量。例如 RPO=0 表示任何已提交事务都不允许丢失;RPO=5s 表示最多容忍最近 5 秒内的数据丢失。RTO 描述的是“允许中断多久”,即从故障发生到服务恢复可用的最长时间。二者共同决定了架构选型:RPO 越小,越依赖同步复制或强一致提交;RTO 越小,越依赖自动故障检测与快速切换。
| 业务场景 | 典型 RPO | 典型 RTO | 可选架构倾向 |
|---|---|---|---|
| 内部报表、离线分析 | 分钟级 | 30 分钟以上 | 异步流复制 + 手动切换 |
| 一般在线业务 | 秒级 | 1~5 分钟 | 异步/同步流复制 + 自动切换 |
| 支付、账务核心 | 0(不允许丢) | 秒级到分钟级 | 同步流复制 + 仲裁 + 自动切换 |
需要强调的是,RPO=0 并不等于“永不丢数据”。同步复制只能保证主库在提交时至少有一个备库确认收到 WAL,但如果备库所在机房整体损毁,而主库也同时不可用,仍然可能无法恢复。因此 RPO 和 RTO 必须结合故障域一起定义:是单机故障、单机房故障,还是区域级故障。
流复制在整体架构中的位置
PostgreSQL 的高可用能力不是单一组件提供的,而是由多个层次协作完成。流复制负责数据层的冗余与同步,它把主库产生的 WAL 记录持续发送到备库,使备库保持与主库近似一致的数据状态。但流复制本身不负责故障检测、不负责切换决策、也不负责客户端重连。这些工作由外部组件承担,例如 Patroni、repmgr、pg_auto_failover,或云厂商的托管高可用服务。
因此,一个完整的 PostgreSQL 高可用架构通常包含四层:
- 数据复制层:流复制(物理复制)或逻辑复制,决定数据冗余程度和 RPO。
- 集群管理层:负责选主、故障检测、配置分发、状态一致性,决定 RTO 和切换可靠性。
- 访问路由层:VIP、HAProxy、PgBouncer、DNS 或客户端多主机连接串,决定客户端如何找到当前主库。
- 监控与仲裁层:Prometheus、etcd、Consul、ZooKeeper 等,提供健康判断和防脑裂能力。
流复制处于最底层,是其他所有能力的基础。没有可靠的复制,切换就没有意义;但只有复制,没有管理和路由,切换也无法真正恢复服务。
常见高可用拓扑与选型考量
在实际工程中,最常见的拓扑有以下几种:
一主一备(异步流复制):结构最简单,主库提交不等待备库确认,RPO 通常为秒级到分钟级,RTO 取决于切换方式。适合对数据丢失有一定容忍度的业务,或作为其他拓扑的基础单元。
一主多备(同步 + 异步混合):主库配置 synchronous_standby_names,要求至少一个备库确认后才提交,实现 RPO=0;同时保留异步备库用于只读扩展或跨机房容灾。这是生产环境中最常见的形态。
级联复制:备库再向下游备库转发 WAL,减少主库网络压力,适合跨地域多级部署。但级联链路越长,末端延迟越大,RPO 评估时需要把每一跳都算进去。
双主/多主:PostgreSQL 原生不支持真正的多主写入,通常需要借助逻辑复制或外部扩展(如 BDR)。这类方案复杂度高、冲突处理困难,除非有明确的多地写入需求,否则不建议作为默认选择。
选型时建议按以下顺序判断:先确定 RPO 和 RTO 的硬性要求,再确定故障域(单机、机架、机房、区域),然后选择复制模式和管理工具,最后设计客户端路由方式。不要先选工具再倒推目标,否则很容易出现“工具支持切换,但业务无法接受丢数据”的错配。
synchronous_commit 和超时参数,防止同步等待拖垮主库。
一个可参考的起步配置示例
假设我们有一个中等规模的在线业务,要求 RPO=0、RTO 控制在 60 秒以内,部署在同城两个机房。一个合理的起步方案是:
# postgresql.conf(主库关键参数示例)
wal_level = replica
max_wal_senders = 10
wal_keep_size = 1GB
synchronous_commit = on
synchronous_standby_names = 'ANY 1 (standby_a, standby_b)'
hot_standby = on
# 备库 recovery 配置示例
primary_conninfo = 'host=10.0.1.10 port=5432 user=replicator'
hot_standby = on
在这个示例中,ANY 1 表示两个备库中任意一个确认即可提交,既保证 RPO=0,又避免单点阻塞。管理层可以选择 Patroni + etcd,路由层使用 HAProxy 或 VIP,监控层用 Prometheus 采集 pg_stat_replication 中的 write_lag、flush_lag、replay_lag 指标。这些参数和组件在后续章节会逐一展开。
本章的目标不是让你立刻搭出一套完整集群,而是先建立判断框架:RPO 决定复制模式,RTO 决定切换能力,故障域决定拓扑形态,业务连续性决定投入成本。后续所有关于流复制配置、故障切换、监控排障的内容,都会围绕这个框架展开。