数据库宕机一分钟,损失可能是几十万。高可用不是可选项,而是生产环境的基本门槛。本文手把手带你搭建一套基于 PostgreSQL 17 流复制 + pg_auto_failover 的高可用集群,实现 RPO < 1 秒、RTO < 30 秒的自动故障切换能力。


一、流复制原理:为什么它是高可用的基石

1.1 传统备份 vs 流复制

很多团队早期使用 pg_dump 做定时备份,看起来"有备份",但一旦真的出故障,往往会发现:

  • 上一次备份是昨晚 2 点,故障发生在今天下午 —— 数据丢了半天
  • 从备份恢复要解压、导入、验证 —— 恢复耗时数小时
  • 备份文件本身损坏 —— 恢复彻底失败

流复制(Streaming Replication)从根本上解决了这些问题:

指标 传统备份 流复制
RPO(最大数据丢失量) 小时级 < 1 秒
RTO(故障恢复时间) 小时级 30 秒内
备库可用性 不可用 可读(热备)
故障切换方式 人工操作 自动切换

1.2 流复制的工作原理

Primary 节点
    ↓  写入数据,生成 WAL(Write-Ahead Log)日志
    ↓  WAL Sender 进程将 WAL 实时推送给备库
Standby 节点
    ↓  WAL Receiver 进程接收 WAL
    ↓  Recovery 进程持续回放 WAL,保持与主库同步
    ↓  可提供只读查询服务(hot_standby = on)

WAL 是 PostgreSQL 的事务日志,先写日志,再写数据。流复制的本质是把这份日志实时传送给备库并回放,让备库的数据状态与主库保持高度一致。


二、架构设计:两节点 + pg_auto_failover

2.1 整体架构

                    ┌─────────────────────────────────────────────┐
                    │           pg_auto_failover Monitor           │
                    │           192.168.1.102:5000                 │
                    └──────────────┬──────────────────────────────┘
                                   │ 心跳检测 / 故障判定
              ┌────────────────────┼────────────────────────┐
              │                    │                        │
    ┌─────────▼──────────┐         │             ┌──────────▼─────────┐
    │   Primary 节点      │◄────────┴────────────►│   Standby 节点     │
    │  192.168.1.100:5432 │    WAL 流复制(实时)   │  192.168.1.101:5432│
    │  读写服务           │                        │  只读查询          │
    └────────────────────┘                        └────────────────────┘

        ↑ 故障时自动切换
    应用层连接串(始终指向当前 Primary)

角色说明:

  • Primary:主库,处理所有读写请求,持续向备库推送 WAL
  • Standby:备库,实时同步主库数据,可承接只读查询,Primary 故障时自动晋升
  • Monitor:pg_auto_failover 监控节点,负责健康检测、故障判定和切换协调

2.2 硬件与网络要求

项目 最低要求 推荐配置
服务器数量 2 台 3 台(含 Monitor,跨可用区)
CPU 2 核 4 核+
内存 4 GB 8 GB+
磁盘 SSD NVMe SSD
主备网络延迟 < 10 ms < 1 ms(同机房)
操作系统 Ubuntu 20.04+ Ubuntu 22.04 LTS

网络延迟是关键:同步复制模式下,主库每次提交都需要等待备库确认,网络延迟直接影响写入性能。推荐主备节点部署在同一机房/同一可用区。


三、详细部署步骤

3.1 环境准备(所有节点)

配置各节点 /etc/hosts(或 DNS),确保节点间通过主机名可互访:

# 在所有节点追加
echo "192.168.1.100  pg-primary" | sudo tee -a /etc/hosts
echo "192.168.1.101  pg-standby" | sudo tee -a /etc/hosts
echo "192.168.1.102  pg-monitor" | sudo tee -a /etc/hosts

3.2 配置主库(Primary)

第一步:创建复制专用用户

sudo -u postgres psql
-- 创建复制用户,权限最小化
CREATE USER replicator WITH REPLICATION ENCRYPTED PASSWORD 'Repl@Strong2026';

-- 验证用户创建
\du replicator

第二步:修改 postgresql.conf

sudo -u postgres vi /etc/postgresql/17/main/postgresql.conf

关键参数:

# ==================== 复制相关 ====================
wal_level = replica            # 开启复制所需的 WAL 级别
max_wal_senders = 5            # 允许的最大 WAL 发送进程数(备库数 + 2)
hot_standby = on               # 允许备库提供只读服务

# ==================== WAL 归档 ====================
archive_mode = on
archive_command = 'cp %p /var/lib/postgresql/17/archive/%f'

# ==================== 超时设置 ====================
wal_sender_timeout = 60s       # WAL 发送超时
wal_receiver_timeout = 60s     # WAL 接收超时

# ==================== 监听地址 ====================
listen_addresses = '*'         # 监听所有网卡(生产环境建议指定 IP)
port = 5432

第三步:修改 pg_hba.conf(访问控制)

sudo -u postgres vi /etc/postgresql/17/main/pg_hba.conf

追加以下内容:

# 允许备库连接复制
host    replication     replicator      192.168.1.101/32        scram-sha-256

# 允许 pg_auto_failover Monitor 连接
host    all             postgres        192.168.1.102/32        scram-sha-256

# 允许应用服务器连接(按实际 IP 修改)
host    all             all             192.168.1.0/24          scram-sha-256

第四步:创建归档目录并重启

# 创建 WAL 归档目录
sudo -u postgres mkdir -p /var/lib/postgresql/17/archive
sudo -u postgres chmod 700 /var/lib/postgresql/17/archive

# 重启主库使配置生效
sudo systemctl restart postgresql

# 验证主库正常运行
sudo -u postgres psql -c "SELECT version();"
sudo -u postgres psql -c "SHOW wal_level;"

3.3 配置备库(Standby)

第一步:停止备库的 PostgreSQL 并清空数据目录

sudo systemctl stop postgresql

# 备份原有配置文件
sudo cp /etc/postgresql/17/main/postgresql.conf /tmp/postgresql.conf.bak
sudo cp /etc/postgresql/17/main/pg_hba.conf /tmp/pg_hba.conf.bak

# 清空数据目录(重要!pg_basebackup 需要目标目录为空)
sudo -u postgres rm -rf /var/lib/postgresql/17/main/*

第二步:从主库拉取基础备份

sudo -u postgres pg_basebackup \
  -h 192.168.1.100 \
  -D /var/lib/postgresql/17/main \
  -U replicator \
  -P -W -R \
  --wal-method=stream \
  --checkpoint=fast

# 参数说明:
# -h  主库 IP
# -D  目标数据目录
# -U  复制用户
# -P  显示进度
# -W  提示输入密码
# -R  自动生成 standby.signal 和 postgresql.auto.conf(复制连接信息)
# --wal-method=stream  同时流式传输 WAL,确保备份一致性

第三步:配置备库 postgresql.conf

sudo -u postgres vi /etc/postgresql/17/main/postgresql.conf
listen_addresses = '*'
port = 5432
hot_standby = on               # 允许备库提供只读查询
hot_standby_feedback = on      # 向主库反馈备库查询状态,防止主库清理备库还在读的数据行
wal_receiver_timeout = 60s

第四步:验证 standby.signal 文件存在

# pg_basebackup -R 会自动创建此文件,表明这是一个备库
ls -la /var/lib/postgresql/17/main/standby.signal

# 查看自动生成的连接配置
cat /var/lib/postgresql/17/main/postgresql.auto.conf
# 应包含:primary_conninfo = 'host=192.168.1.100 user=replicator ...'

第五步:启动备库

sudo systemctl start postgresql

# 查看启动日志
sudo journalctl -u postgresql -n 50 --no-pager

# 正常应包含:
# LOG:  entering standby mode
# LOG:  started streaming WAL from primary

3.4 验证流复制状态

在主库执行:

-- 查看复制连接状态
SELECT
    client_addr,
    usename,
    application_name,
    state,
    sent_lsn,
    write_lsn,
    flush_lsn,
    replay_lsn,
    write_lag,
    flush_lag,
    replay_lag,
    sync_state
FROM pg_stat_replication;

期望输出示例:

  client_addr   | usename    | state     | sync_state | replay_lag
----------------+------------+-----------+------------+------------
 192.168.1.101  | replicator | streaming | async      | 00:00:00

测试数据同步:

-- 在主库创建测试表并插入数据
CREATE TABLE replication_test (id SERIAL PRIMARY KEY, ts TIMESTAMPTZ DEFAULT NOW());
INSERT INTO replication_test DEFAULT VALUES;

-- 在备库验证(需先执行 SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY; 或在只读模式下连接)
-- 连接到备库
sudo -u postgres psql -h 192.168.1.101 -c "SELECT * FROM replication_test;"
-- 应立即看到主库刚插入的数据

3.5 配置 pg_auto_failover(自动故障转移)

pg_auto_failover 引入了一个独立的 Monitor 节点,负责:

  • 持续对主备库进行心跳检测
  • 在检测到主库不可达时,协调备库执行 Failover
  • 防止脑裂(Split-Brain):只有 Monitor 认可的节点才能成为 Primary

Step 1:在 Monitor 节点初始化监控服务

# 在 192.168.1.102 上执行
sudo -u postgres pg_autoctl create monitor \
  --pgdata /var/lib/postgresql/17/monitor \
  --pgport 5000 \
  --hostname 192.168.1.102

# 启动 Monitor
sudo -u postgres pg_autoctl run --pgdata /var/lib/postgresql/17/monitor &

# 查看监控节点状态
sudo -u postgres pg_autoctl show state --pgdata /var/lib/postgresql/17/monitor

Step 2:将主库注册到 Monitor

# 在 192.168.1.100(Primary)上执行
sudo -u postgres pg_autoctl create postgres \
  --pgdata /var/lib/postgresql/17/main \
  --pgport 5432 \
  --hostname 192.168.1.100 \
  --monitor postgresql://autoctl_node@192.168.1.102:5000/pg_auto_failover \
  --name pg-primary

# 启动 pg_autoctl 代理
sudo -u postgres pg_autoctl run --pgdata /var/lib/postgresql/17/main &

Step 3:将备库注册到 Monitor

# 在 192.168.1.101(Standby)上执行
sudo -u postgres pg_autoctl create postgres \
  --pgdata /var/lib/postgresql/17/main \
  --pgport 5432 \
  --hostname 192.168.1.101 \
  --monitor postgresql://autoctl_node@192.168.1.102:5000/pg_auto_failover \
  --name pg-standby

# 启动 pg_autoctl 代理
sudo -u postgres pg_autoctl run --pgdata /var/lib/postgresql/17/main &

Step 4:验证集群状态

# 在任意节点执行
sudo -u postgres pg_autoctl show state \
  --monitor postgresql://autoctl_node@192.168.1.102:5000/pg_auto_failover

期望输出:

  Name        |  Node |       Host:Port        |       LSN        | Reachable | Health | Current State | Assigned State
--------------+-------+------------------------+------------------+-----------+--------+---------------+----------------
  pg-primary  |     1 | 192.168.1.100:5432     | 0/5000000        | yes       | OK     | primary       | primary
  pg-standby  |     2 | 192.168.1.101:5432     | 0/5000000        | yes       | OK     | secondary     | secondary

两个节点均为 OK 状态,集群配置完成。


四、自动故障转移测试

重要:故障转移测试建议在非生产时间窗口进行,并提前通知相关团队。

4.1 计划内切换(手动 Switchover)

计划内维护时(如主库升级、硬件维护),使用手动切换:

# 在任意节点执行手动切换(原备库晋升为 Primary)
sudo -u postgres pg_autoctl perform switchover \
  --monitor postgresql://autoctl_node@192.168.1.102:5000/pg_auto_failover

# 查看切换后的状态
sudo -u postgres pg_autoctl show state \
  --monitor postgresql://autoctl_node@192.168.1.102:5000/pg_auto_failover

手动切换整个过程通常在 10 秒以内完成,业务中断时间极短。

4.2 模拟主库故障(自动 Failover 测试)

# 在 Primary 节点(192.168.1.100)上强制停止数据库
sudo systemctl stop postgresql

# 在 Monitor 节点或 Standby 节点上持续观察状态变化
watch -n 2 "sudo -u postgres pg_autoctl show state \
  --monitor postgresql://autoctl_node@192.168.1.102:5000/pg_auto_failover"

观察到的状态变化过程:

时间 +0s   : pg-primary  →  primary      (正常)
时间 +5s   : pg-primary  →  unhealthy    (Monitor 检测到不可达)
时间 +15s  : pg-primary  →  demoted      (降级确认)
时间 +20s  : pg-standby  →  wait_primary (等待晋升)
时间 +25s  : pg-standby  →  primary      (晋升完成 ✅)

整个自动切换过程约 20-30 秒,期间应用层若使用了连接池(如 PgBouncer)或连接重试机制,可以无感知地完成切换。

4.3 恢复原主库,重新加入集群

# 修复原主库后,重新启动 PostgreSQL
sudo systemctl start postgresql

# 重新注册到集群(原主库会自动变为 Secondary)
sudo -u postgres pg_autoctl run --pgdata /var/lib/postgresql/17/main &

# 验证原主库已以 Secondary 身份加入集群
sudo -u postgres pg_autoctl show state \
  --monitor postgresql://autoctl_node@192.168.1.102:5000/pg_auto_failover

五、监控与告警:生产环境的必要保障

5.1 关键监控指标

指标 查询方法 告警阈值 说明
复制延迟(字节差) pg_stat_replication.write_lag > 100 MB 网络或备库性能问题
复制延迟(时间差) pg_last_xact_replay_timestamp() > 5 秒 备库回放异常
WAL 发送状态 pg_stat_replication.state 非 streaming 复制中断
连接数 pg_stat_activity > max_connections × 80% 连接池告警
磁盘使用率 df -h > 80% WAL 积压风险
pg_autoctl 进程 pgrep pg_autoctl 进程不存在 自动切换能力失效

5.2 复制延迟监控脚本

将以下脚本保存为 /usr/local/bin/check_pg_replication.sh

chmod +x /usr/local/bin/check_pg_replication.sh

# 测试运行
/usr/local/bin/check_pg_replication.sh 192.168.1.101 5 30

5.3 集成 Prometheus + Grafana

# 安装 postgres_exporter
wget https://github.com/prometheus-community/postgres_exporter/releases/latest/download/postgres_exporter_linux_amd64.tar.gz
tar -xzf postgres_exporter_linux_amd64.tar.gz
sudo mv postgres_exporter /usr/local/bin/

# 配置连接信息
export DATA_SOURCE_NAME="postgresql://monitor_user:password@localhost:5432/postgres?sslmode=disable"

# 启动 exporter(默认端口 9187)
postgres_exporter &

Prometheus 配置追加:

scrape_configs:
  - job_name: 'postgresql'
    static_configs:
      - targets: ['192.168.1.100:9187', '192.168.1.101:9187']
    labels:
      cluster: 'pg-ha'

推荐 Grafana Dashboard ID:9628(PostgreSQL Database)和 12485(pg_auto_failover)。


六、性能优化与最佳实践

6.1 内核参数调优

cat >> /etc/sysctl.conf << 'EOF'
# 共享内存(设置为物理内存的 50%)
kernel.shmmax = 274877906944
kernel.shmall = 67108864

# 网络优化
net.core.somaxconn = 1024
net.ipv4.tcp_keepalive_time = 60
net.ipv4.tcp_keepalive_intvl = 10
net.ipv4.tcp_keepalive_probes = 6

# 减少 swap 使用
vm.swappiness = 10
vm.overcommit_memory = 2
vm.overcommit_ratio = 90
EOF

sysctl -p

6.2 PostgreSQL 参数调优

6.3 安全加固

启用 SSL 复制:

# 生成自签名证书(生产环境建议使用 CA 签发)
sudo -u postgres openssl req -new -x509 -days 3650 -nodes \
  -out /etc/postgresql/17/main/server.crt \
  -keyout /etc/postgresql/17/main/server.key \
  -subj "/CN=pg-primary"
sudo chmod 600 /etc/postgresql/17/main/server.key
sudo chown postgres:postgres /etc/postgresql/17/main/server.{crt,key}
# postgresql.conf
ssl = on
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'

强制使用 SCRAM-SHA-256 认证(PG 17 默认值):

# postgresql.conf
password_encryption = scram-sha-256
# pg_hba.conf 中将 md5 全部替换为 scram-sha-256
host    replication     replicator      192.168.1.101/32        scram-sha-256
host    all             all             192.168.1.0/24          scram-sha-256

最小化复制用户权限:

-- 复制用户只需 REPLICATION 权限,不需要数据库访问权限
CREATE USER replicator WITH REPLICATION ENCRYPTED PASSWORD 'xxx';
-- 不要给 replicator 用户 superuser 或 login 数据库的权限

七、常见问题排查

Q1:复制延迟持续增高

现象: pg_stat_replication.replay_lag 不断增大,备库追不上主库

排查步骤:

# 1. 检查网络带宽
iperf3 -c 192.168.1.101 -t 30

# 2. 检查备库磁盘 I/O
iostat -x 1 10

# 3. 查看 WAL 产生速率(主库)
sudo -u postgres psql -c "SELECT pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), sent_lsn)) as lag FROM pg_stat_replication;"

# 4. 检查备库 recovery 进程 CPU 使用率
top -u postgres

解决方案:

  • 升级主备间网络带宽
  • 开启 wal_compression = on 减少传输量
  • 检查备库是否有大量只读查询拖慢 WAL 回放,考虑配置只读副本分流

Q2:故障转移未自动触发

现象: 主库宕机后,备库未自动晋升

排查步骤:

# 1. 检查 Monitor 节点状态
sudo -u postgres pg_autoctl show state --pgdata /var/lib/postgresql/17/monitor

# 2. 查看 Monitor 日志
sudo journalctl -u pg_autoctl -n 100 --no-pager | grep -E "ERROR|WARN|failover"

# 3. 检查 Monitor 与各节点的网络连通性
nc -zv 192.168.1.100 5432
nc -zv 192.168.1.101 5432

# 4. 验证 pg_autoctl 进程是否在所有节点上运行
ps aux | grep pg_autoctl

常见原因及解决:

  • Monitor 节点本身宕机 → Monitor 是单点,生产环境建议使用高可用监控部署
  • pg_autoctl 进程未运行 → 检查 systemd 配置,设置 Restart=always
  • 节点间网络分区 → 检查防火墙规则,确保 Monitor 可达所有节点

Q3:备库无法启动 / 复制连接被拒绝

现象: 备库日志报 FATAL: could not connect to the primary server

排查步骤:

# 1. 确认 pg_hba.conf 中允许了备库 IP
sudo -u postgres psql -c "SHOW hba_file;"
grep replication /etc/postgresql/17/main/pg_hba.conf

# 2. 在备库上手动测试连接
sudo -u postgres psql "host=192.168.1.100 user=replicator dbname=replication sslmode=require"

# 3. 检查 postgresql.auto.conf 中的连接信息
cat /var/lib/postgresql/17/main/postgresql.auto.conf

# 4. 检查 standby.signal 文件是否存在
ls /var/lib/postgresql/17/main/standby.signal

如果以上排查无果,重新拉取基础备份:

sudo systemctl stop postgresql
sudo -u postgres rm -rf /var/lib/postgresql/17/main/*
sudo -u postgres pg_basebackup \
  -h 192.168.1.100 -D /var/lib/postgresql/17/main \
  -U replicator -P -W -R --wal-method=stream
sudo systemctl start postgresql

八、定期演练清单

高可用方案的价值,在于真正故障时能发挥作用。建议每季度执行以下演练:

✅ 手动 Switchover 测试(验证计划内切换正常)
✅ 模拟主库故障(验证自动 Failover 触发时间 ≤ 30 秒)
✅ 恢复原主库并重新加入集群(验证集群恢复能力)
✅ 从备份恢复测试(验证备份有效性和 RTO)
✅ 检查监控告警是否正常触发(避免告警静默失效)
✅ 确认应用层连接重试机制有效(模拟切换后应用自动重连)

总结

本文完整介绍了 PostgreSQL 17 流复制 + pg_auto_failover 高可用集群的部署实践:

能力 实现方式 效果
数据保护 流复制(WAL 实时同步) RPO < 1 秒
自动切换 pg_auto_failover Monitor RTO < 30 秒
只读扩展 hot_standby 备库承接读请求 读写分离
监控告警 Prometheus + Grafana + 自定义脚本 问题提前预警
安全加固 SSL + SCRAM-SHA-256 + 最小权限 符合生产规范

这套架构可以满足 99.9% 的可用性要求(每年宕机 < 8.7 小时)。如需进一步提升,可以扩展为 3 节点跨可用区 部署,将可用性提升至 99.99%。

高可用不是一次性的配置工作,而是持续的运维能力——正确的配置 + 完善的监控 + 定期的演练,才能在真正的故障面前,让系统的自愈能力如预期发挥作用。


参考资料

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐