PostgreSQL流复制(Streaming Replication)是构建数据库高可用架构的基础机制,Patroni在此基础上提供了自动故障检测、主从切换和集群管理的完整方案。在金融、电商等对数据一致性和可用性要求严苛的场景中,Patroni+etcd+HAProxy的组合已成为PostgreSQL高可用的标准实践。本文从流复制的基础配置入手,逐步搭建三节点的Patroni高可用集群,并覆盖故障切换验证和日常运维操作。
PostgreSQL流复制原理与WAL日志传输机制
PostgreSQL通过WAL(Write-Ahead Logging)机制实现流复制。主节点(Primary)将事务日志写入WAL文件后,通过TCP连接将WAL数据流式传输到备节点(Standby),备节点实时重放WAL日志保持数据同步。
-- 主节点配置 (postgresql.conf)
wal_level = replica
max_wal_senders = 10
wal_keep_size = 1024 # 保留1GB WAL日志,避免备节点断连后需要全量重传
hot_standby = on # (备节点配置)允许只读查询
-- 创建复制专用用户
CREATE ROLE repl WITH REPLICATION LOGIN PASSWORD 'repl_secure_pass';
-- pg_hba.conf 添加复制权限
# host replication repl 192.168.1.0/24 md5
wal_level=replica是流复制的最低要求,logical级别还支持逻辑解码。max_wal_senders控制同时连接的备节点数量。wal_keep_size(PostgreSQL 13+替代了wal_keep_segments)指定保留的WAL文件大小,太大会浪费磁盘,太小可能导致备节点断连后无法增量同步而触发全量基础备份。
基础备份与备节点初始化操作流程
备节点的初始化通过pg_basebackup工具从主节点拉取完整数据快照,然后配置为流复制模式启动。
# 在备节点执行基础备份
pg_basebackup \
-h 192.168.1.10 \
-U repl \
-D /var/lib/postgresql/data \
-Fp \
-Xs \
-P \
-R
# -Fp: 以plain格式输出
# -Xs: 使用stream方式传输WAL(与基础备份并行)
# -P: 显示进度
# -R: 自动创建standby.signal文件和连接配置
-R参数会自动在数据目录中创建standby.signal文件(PostgreSQL 12+支持),并写入primary_conninfo连接信息到postgresql.auto.conf。备节点启动后自动进入恢复模式,通过primary_conninfo配置的连接信息从主节点接收WAL流。
# postgresql.auto.conf 自动生成的内容
primary_conninfo = 'user=repl password=repl_secure_pass host=192.168.1.10 port=5432 sslmode=prefer'
primary_slot_name = 'standby_slot'
复制槽管理与WAL积压监控方法
复制槽(Replication Slot)确保主节点不会在备节点收到WAL之前删除对应的WAL文件。未使用复制槽时,如果备节点断连时间较长,主节点可能已循环覆盖WAL文件,导致备节点无法增量恢复。但复制槽也有风险:备节点长期断连时,WAL文件无限堆积导致主节点磁盘占满。
-- 创建物理复制槽
SELECT pg_create_physical_replication_slot('standby_slot');
-- 查看复制槽状态
SELECT slot_name, active, restart_lsn,
pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS lag_bytes
FROM pg_replication_slots;
-- 查看复制连接和延迟
SELECT application_name, state, sync_state,
sent_lsn, write_lsn, flush_lsn, replay_lsn,
pg_wal_lsn_diff(sent_lsn, replay_lsn) AS replay_lag_bytes
FROM pg_stat_replication;
sync_state字段显示同步模式:async表示异步复制(备节点不影响主节点提交),sync表示同步复制(主节点等待至少一个同步备节点确认写入后才提交)。replay_lag_bytes显示备节点重放延迟的字节数,如果持续增长说明备节点处理速度跟不上主节点。
Patroni集群架构设计与etcd配置
Patroni是Zalando开源的PostgreSQL高可用管理工具,依赖etcd/Consul/ZooKeeper作为分布式配置存储实现Leader选举和状态同步。
# etcd配置 (三节点集群,每个节点一个实例)
# /etc/etcd/etcd.conf
name: 'etcd-node1'
data-dir: '/var/lib/etcd'
listen-peer-urls: 'http://192.168.1.10:2380'
listen-client-urls: 'http://192.168.1.10:2379'
initial-cluster: 'etcd-node1=http://192.168.1.10:2380,etcd-node2=http://192.168.1.11:2380,etcd-node3=http://192.168.1.12:2380'
initial-cluster-state: 'new'
initial-cluster-token: 'pg-ha-cluster'
etcd集群部署在PostgreSQL节点相同或独立的服务器上。三节点etcd能容忍单节点故障。Patroni使用etcd的分布式锁机制实现Leader选举:最先获取到锁的节点成为Primary,其余节点自动配置为Standby并连接到当前Primary。
Patroni YAML配置文件详解与调优参数
# /etc/patroni/patroni.yml (节点1)
scope: pg-ha-cluster
name: pg-node1
restapi:
listen: 0.0.0.0:8008
connect_address: 192.168.1.10:8008
etcd:
hosts: 192.168.1.10:2379,192.168.1.11:2379,192.168.1.12:2379
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576 # 1MB,超过则不允许切换
maximum_lag_on_sync: 1048576
synchronous_mode: true # 开启同步复制
synchronous_mode_strict: false # 允许在仅有异步备节点时降级运行
postgresql:
use_pg_rewind: true # 允许使用pg_rewind修复时间线分叉
parameters:
wal_level: replica
max_wal_senders: 10
wal_keep_size: 1024
hot_standby: on
synchronous_commit: on
synchronous_standby_names: '*'
initdb:
- encoding: UTF8
- data-checksums
postgresql:
listen: 0.0.0.0:5432
connect_address: 192.168.1.10:5432
data_dir: /var/lib/postgresql/data
bin_dir: /usr/lib/postgresql/15/bin
authentication:
replication:
username: repl
password: repl_secure_pass
superuser:
username: postgres
password: pg_secure_pass
pg_hba:
- host replication repl 192.168.1.0/24 md5
- host all all 0.0.0.0/0 md5
tags:
nofailover: false
noloadbalance: false
clonefrom: false
ttl=30是Leader租约时长,Patroni每loop_wait=10秒续约一次。如果Primary节点宕机,最多retry_timeout=10秒后etcd租约过期,触发重新选举。maximum_lag_on_failover=1048576(1MB)是安全阈值:备节点延迟超过1MB时不允许提升为Primary,防止数据丢失。synchronous_mode=true要求至少一个同步备节点确认写入后主节点才返回提交成功。
自动故障切换流程与手动运维操作指南
Patroni通过REST API提供集群管理接口,patronictl命令行工具封装了常用操作。
# 查看集群状态
patronictl -c /etc/patroni/patroni.yml list
# 输出示例
# + Cluster: pg-ha-cluster ----+---------+----+-----------+
# | Member | Host | Role | State | TL | Lag in MB |
# | pg-node1 | 192.168.1.10 | Leader | running | 5 | |
# | pg-node2 | 192.168.1.11 | Replica | streaming| 5 | 0 |
# | pg-node3 | 192.168.1.12 | Replica | streaming| 5 | 0 |
# +-----------+----------------+---------+----------+----+-----------+
# 手动切换主节点(计划性维护)
patronictl switchover -c /etc/patroni/patroni.yml \
--master pg-node1 --candidate pg-node2
# 重新初始化故障节点(数据损坏后修复)
patronictl reinit -c /etc/patroni/patroni.yml pg-ha-cluster pg-node3
# 暂停自动故障切换(运维窗口期)
patronictl pause -c /etc/patroni/patroni.yml
# 恢复自动故障切换
patronictl resume -c /etc/patroni/patroni.yml
switchover是计划性切换,先验证候选节点的复制延迟为零,然后优雅降级当前Leader、提升候选节点为新Leader,整个过程在数秒内完成。failover是非计划性的:Leader节点不可用时自动触发,Patroni在各Standby中选择延迟最小的节点提升为Leader。已宕机的原Leader恢复后,Patroni通过pg_rewind合并时间线分叉,将其重新接入集群为Standby。
HAProxy配置读写分离路由:
frontend pg_read_write
bind *:5432
mode tcp
use_backend pg_primary
frontend pg_read_only
bind *:5433
mode tcp
use_backend pg_standby
backend pg_primary
mode tcp
option tcp-check
# Patroni REST API健康检查:只有Leader返回200
tcp-check connect port 8008
tcp-check send GET\ /primary\ HTTP/1.1\r\n\r\n
tcp-check expect string 200
server pg-node1 192.168.1.10:5432 check
server pg-node2 192.168.1.11:5432 check
server pg-node3 192.168.1.12:5432 check
backend pg_standby
mode tcp
option tcp-check
tcp-check connect port 8008
tcp-check send GET\ /replica\ HTTP/1.1\r\n\r\n
tcp-check expect string 200
server pg-node1 192.168.1.10:5432 check
server pg-node2 192.168.1.11:5432 check
server pg-node3 192.168.1.12:5432 check
5432端口路由到Primary节点,5433端口号路由到Standby节点进行读负载均衡。Patroni的/primary接口只在Leader节点返回200,/replica接口只在Standby节点返回200,HAProxy据此自动识别角色变化。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/postgresql-liu-fu-zhi-pei-zhi-yu-patroni-gao-ke-yong-ji-qun/