PostgreSQL流复制(Streaming Replication)通过实时传输WAL日志实现主从同步,是构建数据库高可用架构的基础。Patroni作为开源的PostgreSQL高可用管理工具,基于etcd实现自动故障检测和主节点切换,结合HAProxy可实现无感知的数据库故障转移。以下是从单机搭建到高可用集群的完整实战配置。
PostgreSQL流复制原理
PostgreSQL流复制的核心是WAL(Write-Ahead Logging)机制。主节点将事务日志写入WAL文件后,通过TCP连接实时发送给备节点,备节点接收WAL日志并回放,实现数据同步。根据同步模式不同,分为同步复制和异步复制:
异步复制(async) - 主节点写入WAL后立即返回,不等备节点确认
优点:低延迟 缺点:主节点宕机可能丢失少量数据
同步复制(sync) - 主节点等待至少一个备节点确认收到WAL后才返回
优点:零数据丢失 缺点:备节点故障时主节点阻塞
生产环境通常采用”一主一同步备+一异步备”的架构,平衡数据安全和可用性。
主节点配置
修改postgresql.conf:
# postgresql.conf
listen_addresses = '*'
port = 5432
# WAL配置
wal_level = replica
max_wal_senders = 10
wal_keep_size = 1024 # 保留1GB WAL
hot_standby = on
# 同步复制配置
synchronous_standby_names = 'FIRST 1 (sync_standby)'
# 归档配置
archive_mode = on
archive_command = 'test ! -f /archive/%f && cp %p /archive/%f'
# 连接配置
max_connections = 200
shared_buffers = 4GB
effective_cache_size = 12GB
work_mem = 64MB
maintenance_work_mem = 512MB
创建复制用户:
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'StrongRepPass2026';
修改pg_hba.conf允许备节点连接:
# pg_hba.conf
# TYPE DATABASE USER ADDRESS METHOD
host replication replicator 192.168.1.0/24 scram-sha-256
host all all 192.168.1.0/24 scram-sha-256
备节点搭建
使用pg_basebackup从主节点创建备节点:
# 停止备节点PostgreSQL
systemctl stop postgresql
# 清空备节点数据目录
rm -rf /var/lib/postgresql/15/main/*
# 从主节点拉取基础备份
pg_basebackup \
-h 192.168.1.10 \
-U replicator \
-D /var/lib/postgresql/15/main \
-Fp \
-Xs \
-P \
-R # 自动创建standby.signal和primary.conninfo
# 验证standby.signal文件
ls -la /var/lib/postgresql/15/main/standby.signal
# 检查primary_conninfo配置
cat /var/lib/postgresql/15/main/postgresql.auto.conf
# 应包含:
# primary_conninfo = 'user=replicator password=... host=192.168.1.10 port=5432 sslmode=prefer'
# 启动备节点
systemctl start postgresql
验证复制状态:
-- 主节点执行:查看复制状态
SELECT
pid,
usename,
application_name,
client_addr,
state,
sync_state,
sent_lsn,
write_lsn,
flush_lsn,
replay_lsn,
write_lag,
flush_lag,
replay_lag
FROM pg_stat_replication;
-- 备节点执行:确认处于恢复模式
SELECT pg_is_in_recovery(); -- 返回true
-- 备节点执行:查看接收和回放的WAL位置
SELECT
pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS lag_bytes,
pg_last_wal_receive_lsn() AS receive_lsn,
pg_last_wal_replay_lsn() AS replay_lsn,
pg_last_xact_replay_timestamp() AS last_replay_time;
Patroni自动故障转移配置
Patroni依赖分布式配置存储(etcd)管理集群状态。以etcd为例,在所有节点安装Patroni:
pip3 install patroni[etcd]
各节点Patroni配置 /etc/patroni/patroni.yml:
scope: pg-cluster
namespace: /service/
name: 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
synchronous_mode: true
synchronous_mode_strict: false
postgresql:
use_pg_rewind: true
parameters:
wal_level: replica
hot_standby: on
max_wal_senders: 10
wal_keep_size: 1024
initdb:
- encoding: UTF8
- data-checksums
- locale: C
postgresql:
listen: 0.0.0.0:5432
connect_address: 192.168.1.10:5432
data_dir: /var/lib/postgresql/15/main
bin_dir: /usr/lib/postgresql/15/bin
authentication:
superuser:
username: postgres
password: SuperSecretPass2026
replication:
username: replicator
password: StrongRepPass2026
rewind:
username: rewinder
password: RewindPass2026
parameters:
shared_buffers: 4GB
effective_cache_size: 12GB
max_connections: 200
启动Patroni集群:
# 在三个节点上分别执行
systemctl start patroni
systemctl enable patroni
# 查看集群状态
patronictl -c /etc/patroni/patroni.yml list
# 输出示例:
# + Cluster: pg-cluster ---+---------+----+-----------+
# | Member | Host | Role | State | TL | Lag in MB |
# +--------+-------------+---------+-----------+----+-----------+
# | node1 | 192.168.1.10| Leader | running | 1 | |
# | node2 | 192.168.1.11| Replica | streaming | 1 | 0 |
# | node3 | 192.168.1.12| Replica | streaming | 1 | 0 |
# +--------+-------------+---------+-----------+----+-----------+
手动switchover与自动failover
计划内切换(switchover):将主节点切换到指定备节点,不丢数据:
# 手动切换主节点到node2
patronictl switchover -c /etc/patroni/patroni.yml
# 交互式选择
# Primary [node1]:
# Candidate []: node2
# Are you sure you want to switch leaders? [y/N]: y
自动故障转移(failover):主节点宕机后Patroni自动选举新主:
# 模拟主节点故障
systemctl stop patroni # 在node1上执行
# 等待TTL超时(30秒)后,Patroni自动选举node2为新主
patronictl -c /etc/patroni/patroni.yml list
# node1恢复后自动加入集群作为备节点
systemctl start patroni
自动故障转移的选举逻辑:Patroni根据WAL LSN位置选择数据最新的备节点作为新主。如果所有备节点的LSN都落后于主节点最后的LSN,且maximum_lag_on_failover配置不满足,则拒绝故障转移以避免数据丢失。
HAProxy读写分离配置
# haproxy.cfg
frontend pg_write_frontend
bind *:5432
default_backend pg_write_backend
frontend pg_read_frontend
bind *:5433
default_backend pg_read_backend
backend pg_write_backend
mode tcp
option tcp-check
server node1 192.168.1.10:5432 check port 8008
server node2 192.168.1.11:5432 check port 8008
server node3 192.168.1.12:5432 check port 8008
backend pg_read_backend
mode tcp
balance roundrobin
server node1 192.168.1.10:5432 check
server node2 192.168.1.11:5432 check
server node3 192.168.1.12:5432 check
Patroni的REST API端点(8008端口)提供健康检查接口:
# 检查是否为主节点(返回200)
curl -s http://192.168.1.10:8008/primary -o /dev/null -w "%{http_code}"
# 检查是否为可读备节点(返回200)
curl -s http://192.168.1.10:8008/replica -o /dev/null -w "%{http_code}"
# 获取集群状态JSON
curl -s http://192.168.1.10:8008/cluster | python3 -m json.tool
pg_rewind修复脑裂后的数据同步
脑裂场景:网络分区导致旧主节点继续接受写入,恢复后其WAL timeline与新主分叉。需要使用pg_rewind回退到分叉点后重新同步:
# 在旧主节点(node1)上执行
systemctl stop postgresql
# pg_rewind回退到新主的分叉点
pg_rewind \
--target-data=/var/lib/postgresql/15/main \
--source-server="host=192.168.1.11 user=postgres password=SuperSecretPass2026"
# 创建standby.signal
touch /var/lib/postgresql/15/main/standby.signal
# 启动Patroni,自动以备节点身份加入集群
systemctl start patroni
Patroni配置中use_pg_rewind: true允许自动执行pg_rewind,无需人工干预。pg_rewind通过比较两个节点的WAL历史找到最后共同点,将旧主的数据文件回退到该点,然后从新主同步差异部分。整个过程比重新pg_basebackup快得多,特别适合大数据库的脑裂恢复。
监控与告警配置
通过Patroni的Prometheus exporter采集集群指标:
# prometheus.yml
scrape_configs:
- job_name: 'patroni'
static_configs:
- targets:
- 192.168.1.10:8008
- 192.168.1.11:8008
- 192.168.1.12:8008
metrics_path: /metrics
关键监控指标:
patroni_cluster_unlocked # 集群是否处于无主状态(应为0)
patroni_postgres_running # PostgreSQL是否运行(应为1)
patroni_postgres_role # 节点角色(1=master, 0=replica)
patroni_postgres_xlog_location # WAL位置(用于计算复制延迟)
patroni_dcs_last_seen # 最后一次与DCS通信时间
告警规则示例:
groups:
- name: patroni
rules:
- alert: PatroniClusterUnlocked
expr: patroni_cluster_unlocked == 1
for: 1m
labels:
severity: critical
annotations:
summary: "PostgreSQL集群无主节点"
- alert: PatroniReplicationLag
expr: max(patroni_postgres_xlog_location) - min(patroni_postgres_xlog_location) > 10485760
for: 5m
labels:
severity: warning
annotations:
summary: "复制延迟超过10MB"
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/postgresql-gao-ke-yong-shi-zhan-liu-fu-zhi-pei-zhi-yu/