PostgreSQL 是当下最流行的开源关系型数据库之一,但当业务流量增长到单机无法承载时,高可用就成了必须跨过的门槛。本文将围绕 Patroni 与 etcd 方案,详解从主从复制到自动故障转移的完整搭建过程,并剖析其中的选举机制与脑裂防护策略。
一、为什么需要Patroni
原生的 PostgreSQL 流复制只提供数据同步能力,主库宕机后需要人工介入执行提升操作。Patroni 是由 Zalando 开源的 PostgreSQL 高可用管理器,它通过一个分布式配置存储(etcd、Consul 或 ZooKeeper)来选举主库,并自动完成提升、重定向、重新加入等动作,实现真正的 RTO 秒级故障转移。
二、环境拓扑与前置准备
典型的三节点集群拓扑如下:三台服务器各运行一个 PostgreSQL 实例和一个 Patroni 进程,另部署一组三节点 etcd 集群提供共识服务。节点规划示例如下:
# 三节点规划
pg1: 192.168.1.11 (初始主库)
pg2: 192.168.1.12 (副本)
pg3: 192.168.1.13 (副本)
# etcd 集群部署在 pg1/pg2/pg3 三节点上
首先确保每台机器安装好 PostgreSQL 16 和 etcd,并在三个节点初始化 etcd 集群:
# 在 pg1 上启动 etcd
etcd --name etcd1 \
--initial-advertise-peer-urls http://192.168.1.11:2380 \
--listen-peer-urls http://0.0.0.0:2380 \
--listen-client-urls http://0.0.0.0:2379 \
--advertise-client-urls http://192.168.1.11:2379 \
--initial-cluster etcd1=http://192.168.1.11:2380,etcd2=http://192.168.1.12:2380,etcd3=http://192.168.1.13:2380 \
--initial-cluster-token pg-ha-cluster \
--initial-cluster-state new
三、Patroni配置详解
每个节点的 Patroni 配置文件 patroni.yml 中,最关键的几个段落是:
scope: pg-ha-cluster
name: pg1 # 每个节点不同
restapi:
listen: 0.0.0.0:8008
connect_address: 192.168.1.11:8008
etcd3:
hosts: 192.168.1.11:2379,192.168.1.12:2379,192.168.1.13:2379
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 20
maximum_lag_on_failover: 1048576
postgresql:
use_pg_rewind: true
parameters:
wal_level: replica
hot_standby: "on"
max_wal_senders: 10
max_replication_slots: 10
wal_keep_size: 1024
postgresql:
listen: 0.0.0.0:5432
connect_address: 192.168.1.11:5432
data_dir: /var/lib/postgresql/data
bin_dir: /usr/lib/postgresql/16/bin
authentication:
replication:
username: replicator
password: Rep1!c@t0rPwd
superuser:
username: postgres
password: Sup3rAdm1nPwd
parameters:
shared_buffers: 4GB
effective_cache_size: 12GB
几个关键配置点:TTL 决定租约时长,loop_wait 是轮询间隔,maximum_lag_on_failover 确保延迟过大的副本不会被提升为主库,use_pg_rewind 让旧主库在故障后能快速重新加入而非全量重建。
四、初始化与故障转移演练
启动 Patroni 后,集群会自动完成主库初始化和副本搭建,可用 patronictl 查看状态:
patronictl -c patroni.yml list
+---------+-------------+---------+-----------+----+-----------+
| Member | Host | Role | State | TL | Lag in MB |
+---------+-------------+---------+-----------+----+-----------+
| pg1 | 192.168.1.11 | Leader | running | 1 | |
| pg2 | 192.168.1.12 | Replica | streaming | 1 | 0 |
| pg3 | 192.168.1.13 | Replica | streaming | 1 | 0 |
+---------+-------------+---------+-----------+----+-----------+
模拟主库故障:在 pg1 上强制结束 postgres 主进程,Patroni 检测到后会在 1 到 2 个 TTL 周期内把延迟最小的副本提升为新主库,整个过程无需人工干预。
五、脑裂防护与读写分离
分布式共识是脑裂防护的核心:etcd 的 Raft 协议确保同一时刻只有一个节点持有 leader 租约。Patroni 在获取主库身份前必须先拿到 etcd 中的 leader key,租约过期前其他节点无法抢占,从而避免双主。
前端接入建议用 HAProxy 或 pgBouncer 做连接路由:通过 Patroni 的 8008 端口健康检查接口区分主库与副本,写流量打到主库,读流量可分发到副本。配置示例:
# HAProxy 后端配置
backend pg_write
option httpchk GET /primary
server pg1 192.168.1.11:5432 check port 8008
server pg2 192.168.1.12:5432 check port 8008
server pg3 192.168.1.13:5432 check port 8008
backend pg_read
option httpchk GET /replica
server pg2 192.168.1.12:5432 check port 8008
server pg3 192.168.1.13:5432 check port 8008
需要强调的是,读副本存在复制延迟,对一致性敏感的读操作仍应走主库。此外定期演练故障转移过程、监控复制延迟与 etcd 集群健康度,才能真正让高可用方案在关键时刻靠得住。