一、为什么需要热备方案
想象一下这样的场景:半夜三点,数据库突然宕机,报警电话把你从睡梦中惊醒。这时候如果有一套完善的热备方案,你只需要翻个身继续睡,系统会自动切换到备用节点,业务完全不受影响。这就是热备的魅力——让故障转移变得像呼吸一样自然。
PostgreSQL作为企业级开源数据库,提供了多种高可用方案。我们今天要聊的,就是如何用流复制(Streaming Replication)实现秒级故障转移。这就像给数据库上了个"复活甲",主库挂掉的瞬间,备库立即顶上。
二、流复制的工作原理
PostgreSQL的流复制核心是WAL(Write-Ahead Logging)机制。主库把数据变更记录到WAL日志,备库实时接收并重放这些日志。整个过程就像主库在直播自己的操作,备库则是个忠实观众,边看边模仿。
来看个配置示例(技术栈:PostgreSQL 12):
-- 在主库创建复制账号
CREATE ROLE replicator WITH LOGIN REPLICATION PASSWORD 'securepassword';
-- 修改pg_hba.conf添加访问规则
# TYPE DATABASE USER ADDRESS METHOD
host replication replicator 192.168.1.0/24 md5
-- 修改postgresql.conf关键参数
wal_level = replica # 启用WAL日志
max_wal_senders = 5 # 最大并发复制连接数
wal_keep_segments = 32 # 保留的WAL段数量
备库的恢复配置(recovery.conf):
standby_mode = 'on'
primary_conninfo = 'host=192.168.1.100 port=5432 user=replicator password=securepassword'
restore_command = 'cp /var/lib/pgsql/wal_archive/%f %p' # WAL归档恢复命令
trigger_file = '/tmp/promote_to_primary' # 触发提升为主动的文件
三、实战搭建热备集群
让我们从零开始搭建一套1主2备的集群。假设三台服务器IP分别为192.168.1.100(主)、192.168.1.101(备1)、192.168.1.102(备2)。
首先在主库执行初始化:
# 在主库生成基础备份
pg_basebackup -h 192.168.1.100 -U replicator -D /var/lib/pgsql/12/data -Fp -Xs -P -R
# 在备库启动服务前配置恢复参数
echo "primary_conninfo = 'host=192.168.1.100 port=5432 user=replicator password=securepassword'" >> /var/lib/pgsql/12/data/recovery.conf
关键监控SQL(三台服务器都适用):
-- 查看复制状态
SELECT client_addr, state, sync_priority, sync_state
FROM pg_stat_replication;
-- 检查WAL延迟
SELECT pg_current_wal_lsn() - replay_lsn AS replication_lag
FROM pg_stat_replication;
四、自动故障转移方案
单纯的主备复制还不够,我们需要自动故障检测和切换。这里引入Patroni作为集群管理工具(技术栈:PostgreSQL + Patroni + etcd)。
Patroni配置文件示例(/etc/patroni.yml):
scope: pg_cluster
namespace: /service/
name: node1
restapi:
listen: 0.0.0.0:8008
connect_address: 192.168.1.100:8008
etcd:
hosts: 192.168.1.100:2379,192.168.1.101:2379,192.168.1.102:2379
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576
postgresql:
use_pg_rewind: true
parameters:
wal_level: replica
hot_standby: "on"
wal_keep_segments: 32
max_wal_senders: 5
故障转移测试脚本:
#!/bin/bash
# 模拟主库宕机
ssh postgres@192.168.1.100 "sudo systemctl stop postgresql-12"
# 观察Patroni日志
tail -f /var/log/patroni.log
# 验证新主库
curl http://192.168.1.101:8008/patroni | jq .role
五、应用层无缝连接
数据库切换了,应用怎么自动跟上?这就要靠连接池的智能路由了。以PgBouncer为例:
配置示例(/etc/pgbouncer/pgbouncer.ini):
[databases]
mydb = host=192.168.1.100,192.168.1.101,192.168.1.102 port=5432 dbname=mydb
[pgbouncer]
pool_mode = transaction
server_check_query = SELECT 1
server_fast_close = 1
server_round_robin = 1
ignore_startup_parameters = extra_float_digits
应用连接字符串只需指向PgBouncer:
# Python连接示例
import psycopg2
conn = psycopg2.connect(
host='pgbouncer-host',
port=6432,
dbname='mydb',
user='appuser',
password='apppassword'
)
六、性能优化与监控
热备集群建好了,但要让其高效运行还需要调优:
- WAL传输压缩(PostgreSQL 13+):
ALTER SYSTEM SET wal_compression = on;
- 并行恢复加速:
ALTER SYSTEM SET max_standby_streaming_delay = 30s;
ALTER SYSTEM SET max_logical_replication_workers = 4;
- 关键监控指标:
# 使用Prometheus收集指标
pg_stat_replication_lag{instance=~"$instance"} > 100000000 # 延迟超过100MB告警
pg_up{instance=~"$instance"} == 0 # 实例宕机检测
七、常见问题解决方案
在实际运维中,你可能会遇到这些问题:
问题1:备库长时间不同步
-- 检查复制槽状态
SELECT slot_name, active, restart_lsn FROM pg_replication_slots;
-- 重建复制槽
SELECT pg_create_physical_replication_slot('standby1_slot');
问题2:主备切换后原主库无法自动加入
# 使用pg_rewind同步数据
pg_rewind --target-pgdata=/var/lib/pgsql/12/data \
--source-server="host=192.168.1.101 port=5432 user=postgres"
八、技术方案对比
让我们看看几种常见方案的优劣:
流复制+手动切换
- 优点:配置简单,资源消耗低
- 缺点:需要人工干预,恢复时间不可控
Patroni+etcd自动切换
- 优点:全自动故障转移,30秒内完成切换
- 缺点:需要额外维护etcd集群
商业方案(如Repmgr)
- 优点:提供图形化监控界面
- 缺点:商业许可费用较高
九、最佳实践建议
根据多年运维经验,我总结出这些黄金法则:
- 永远监控复制延迟,设置合理的阈值告警
- 定期测试故障转移,至少每季度一次完整演练
- 备库硬件配置不应低于主库,特别是IO性能
- 跨机房部署时,考虑使用WAL压缩减少带宽占用
- 重要系统建议采用"主-备-备"的三节点架构
十、未来发展方向
PostgreSQL的高可用方案还在不断进化:
- 物理复制+逻辑复制混合模式:既保证数据一致性,又能做部分表同步
- 多主复制:像MongoDB那样实现真正的多写
- 云原生集成:与Kubernetes Operator深度整合
评论