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 等,提供健康判断和防脑裂能力。

流复制处于最底层,是其他所有能力的基础。没有可靠的复制,切换就没有意义;但只有复制,没有管理和路由,切换也无法真正恢复服务。

PostgreSQL 高可用四层协作架构 流复制提供数据冗余,管理层决策切换,路由层引导客户端,监控层保障判断 监控与仲裁层 Prometheus etcd / Consul 健康检查 防脑裂仲裁 告警通知 访问路由层 VIP / Keepalived HAProxy PgBouncer DNS 切换 多主机连接串 集群管理层 Patroni repmgr pg_auto_failover 选主与切换决策 配置分发 数据复制层(流复制) Primary WAL 写入 / 提交 接受读写连接 Standby 1 WAL 接收 / 重放 只读查询 Standby 2 WAL 接收 / 重放 只读查询 WAL WAL 流 RPO 由复制模式决定(同步/异步) · RTO 由管理层与路由层共同决定

常见高可用拓扑与选型考量

在实际工程中,最常见的拓扑有以下几种:

一主一备(异步流复制):结构最简单,主库提交不等待备库确认,RPO 通常为秒级到分钟级,RTO 取决于切换方式。适合对数据丢失有一定容忍度的业务,或作为其他拓扑的基础单元。

一主多备(同步 + 异步混合):主库配置 synchronous_standby_names,要求至少一个备库确认后才提交,实现 RPO=0;同时保留异步备库用于只读扩展或跨机房容灾。这是生产环境中最常见的形态。

级联复制:备库再向下游备库转发 WAL,减少主库网络压力,适合跨地域多级部署。但级联链路越长,末端延迟越大,RPO 评估时需要把每一跳都算进去。

双主/多主:PostgreSQL 原生不支持真正的多主写入,通常需要借助逻辑复制或外部扩展(如 BDR)。这类方案复杂度高、冲突处理困难,除非有明确的多地写入需求,否则不建议作为默认选择。

选型时建议按以下顺序判断:先确定 RPO 和 RTO 的硬性要求,再确定故障域(单机、机架、机房、区域),然后选择复制模式和管理工具,最后设计客户端路由方式。不要先选工具再倒推目标,否则很容易出现“工具支持切换,但业务无法接受丢数据”的错配。

选型提示:如果业务明确要求 RPO=0,必须使用同步复制,并且至少配置两个备库作为同步候选,避免单个备库故障导致主库提交阻塞。同时要设置合理的 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 决定切换能力,故障域决定拓扑形态,业务连续性决定投入成本。后续所有关于流复制配置、故障切换、监控排障的内容,都会围绕这个框架展开。