外观
PostgreSQL 主从切换机制
主从切换(Failover)是 PostgreSQL 高可用架构中的核心机制,直接决定了主库故障时服务恢复的速度和数据一致性。无论是手动触发的计划性维护,还是自动化工具执行的故障恢复,掌握主从切换的原理和最佳实践对 DBA 至关重要。
主从切换概述
什么是主从切换
主从切换是指在 PostgreSQL 主从复制架构中,将一个从库提升为新主库,并重新配置其他从库连接到新主库的完整过程。这一过程确保了数据库服务在主库故障或维护时的连续性。
主从切换的类型
- 手动切换:DBA 手动执行的切换操作,适用于计划性维护(如版本升级、硬件更换)
- 自动切换:由自动化工具(如 Patroni、repmgr、pg_auto_failover)自动检测故障并执行
- 受控切换:特殊的手动切换,确保主从数据完全一致,用于对数据一致性要求极高的场景
主从切换的核心目标
- 最小化停机时间:减少业务中断时间
- 确保数据一致性:避免数据丢失或分裂
- 维持服务可用性:保证应用程序持续访问
- 简化操作流程:降低切换复杂度和出错风险
手动主从切换
手动主从切换适用于计划性维护场景,DBA 可以完全控制切换过程,确保数据一致性。
生产环境准备工作
检查复制拓扑状态:
sql-- 主库检查所有从库状态 SELECT application_name, state, sent_lsn, write_lsn, flush_lsn, replay_lsn, sync_state FROM pg_stat_replication; -- 从库检查自身状态 SELECT pg_is_in_recovery(), pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn(), now() - pg_last_xact_replay_timestamp() AS replication_lag;业务流量切换准备:
- 通知业务团队维护窗口
- 切换负载均衡器至只读模式(如适用)
- 停止主库写入操作
确保数据一致性:
sql-- 主库设置为只读,防止新写入 ALTER SYSTEM SET default_transaction_read_only = on; SELECT pg_reload_conf(); -- 等待所有从库追上主库(PostgreSQL 14+) SELECT pg_wal_replay_wait_lsn(pg_current_wal_lsn()); -- PostgreSQL 13及以下,手动检查从库LSN -- 在主库执行:SELECT pg_current_wal_lsn(); -- 在从库执行:SELECT pg_last_wal_replay_lsn(); -- 确保两者值相等
执行手动切换步骤
方法一:使用 pg_ctl promote
bash
# 在选定的从库上执行提升命令
pg_ctl promote -D /var/lib/pgsql/16/data
# 验证提升结果
psql -U postgres -c "SELECT pg_is_in_recovery(), pg_current_wal_lsn();"方法二:使用 SQL 命令(PostgreSQL 12+)
sql
-- 在从库执行,等待60秒确保提升完成
SELECT pg_promote(wait_seconds => 60, write_promote_file => true);方法三:手动创建 promote 文件(PostgreSQL 10-11)
bash
# PostgreSQL 10-11 使用 recovery.conf,需创建 promote 文件
touch /var/lib/pgsql/11/data/recovery.done/promote集群重配置与验证
更新其他从库指向新主库:
bash# PostgreSQL 12+ 使用 postgresql.auto.conf vi /var/lib/pgsql/16/data/postgresql.auto.conf # 修改 primary_conninfo 指向新主库 primary_conninfo = 'host=new-master.example.com port=5432 user=replicator password=rep-pass application_name=replica2' # 重启从库或重新加载配置 pg_ctl restart -D /var/lib/pgsql/16/data # PostgreSQL 10-11 修改 recovery.conf vi /var/lib/pgsql/11/data/recovery.conf # 修改 primary_conninfo 后重启验证新集群状态:
sql-- 新主库检查从库连接 SELECT application_name, state, sync_state FROM pg_stat_replication; -- 所有从库验证连接状态 SELECT pg_is_in_recovery(), pg_last_wal_replay_lsn();恢复业务流量:
- 更新负载均衡器指向新主库
- 恢复主库读写模式
sqlALTER SYSTEM RESET default_transaction_read_only; SELECT pg_reload_conf();
手动切换的优缺点
| 优点 | 缺点 |
|---|---|
| 完全可控,确保数据一致性 | 需人工干预,响应时间长 |
| 适合计划性维护 | 依赖DBA经验,操作复杂 |
| 可提前准备,风险低 | 不适合突发故障场景 |
自动主从切换
自动主从切换由高可用工具自动执行,适用于主库突发故障场景,可快速恢复服务。
自动切换工作原理
- 故障检测:工具通过心跳、连接或状态检查监控主库健康
- 故障确认:通过多节点确认或延迟检测避免误判
- 从库选择:根据预定义策略选择最优从库
- 提升操作:将选定从库提升为主库
- 集群重配置:更新其他从库连接
- 客户端重定向:更新负载均衡或DNS
主流自动化工具对比
| 工具 | 故障检测 | 从库选择策略 | 切换速度 | 复杂性 | 生产适用性 | PostgreSQL 16支持 |
|---|---|---|---|---|---|---|
| Patroni | 基于DCS心跳 | 优先级+延迟+健康状态 | 秒级 | 中 | 高 | ✅ |
| repmgr | 直接连接检查 | 优先级+延迟 | 秒级到分钟级 | 低 | 中 | ✅ |
| pg_auto_failover | 专用Monitor监控 | 延迟+健康状态 | 秒级 | 低 | 中 | ✅ |
| Stolon | 基于DCS | 自定义策略 | 秒级 | 高 | 中 | ✅ |
生产环境工具选择建议
- 大规模集群:优先选择 Patroni,支持复杂拓扑和自定义策略
- 中小规模集群:repmgr 或 pg_auto_failover,配置简单易维护
- 云原生环境:Patroni 或 Stolon,与Kubernetes集成良好
自动切换的优缺点
| 优点 | 缺点 |
|---|---|
| 响应迅速(秒级) | 存在误判风险 |
| 无需人工干预 | 配置复杂,依赖外部组件 |
| 适合突发故障 | 网络分区下可能导致脑裂 |
| 降低DBA夜间运维负担 | 数据一致性依赖复制配置 |
主从切换过程详解
故障检测机制
检测方法
- 心跳检测:定期发送心跳包,超时未响应判定为故障
- 连接检测:尝试建立数据库连接,失败次数达到阈值触发切换
- 状态检测:监控主库进程状态、磁盘空间等指标
- 复制延迟检测:持续复制延迟超过阈值判定主库异常
生产环境检测策略
- 多维度检测:结合多种检测方法,避免单一指标误判
- 延迟确认:设置合理的检测间隔(如5秒)和重试次数(如3次)
- 分区感知:使用多数派决策(如Patroni的DCS仲裁)防止脑裂
- 告警前置:复制延迟达到切换阈值前提前告警,预留处理时间
从库选择策略
核心选择标准
- 复制延迟:优先选择延迟最小的从库
- 健康状态:排除CPU/内存/磁盘异常的从库
- 硬件配置:选择与原主库配置相当的从库
- 优先级设置:人工预设从库优先级
- 地理位置:跨区域部署时选择同一区域内的从库
生产环境最佳实践
- 明确优先级:为每个从库设置明确的优先级,避免随机选择
- 考虑硬件差异:确保新主库能承载业务流量
- 地理位置优化:跨区域部署时,优先选择业务流量所在区域的从库
- 避免单点依赖:不要让所有从库依赖同一网络或存储设备
从库提升过程
- 停止复制进程:终止WAL接收和重放线程
- 触发提升:创建promote文件或执行pg_promote()
- 完成恢复:应用所有已接收的WAL日志
- 切换模式:修改内部状态为可写模式
- 更新系统目录:标记新主库身份,清理复制相关设置
- 启动复制服务:开始接受其他从库的复制连接
集群重配置
- 更新从库连接:修改所有从库的primary_conninfo指向新主库
- 重启复制:重新建立从库到新主库的复制连接
- 验证拓扑:确认所有从库正常连接到新主库
- 客户端切换:更新负载均衡器或DNS配置
- 监控恢复:观察新主库性能和从库复制状态
主从切换的最佳实践
1. 预先规划与配置
- 制定详细切换计划:包含步骤、角色、责任人和回滚方案
- 定义切换触发条件:明确手动/自动切换的触发场景和阈值
- 配置合理的复制参数:sql
-- 生产环境推荐配置 synchronous_standby_names = 'ANY 1 (replica1, replica2)' -- 半同步复制 wal_keep_size = '16GB' -- 保留足够WAL日志 max_wal_senders = 10 -- 根据从库数量调整 hot_standby = on -- 从库只读
2. 监控与告警
核心监控指标:
- 主从复制延迟(replication lag)
- 主库状态(pg_is_in_recovery)
- WAL日志生成速率
- 从库连接状态
- 复制槽使用情况
告警阈值设置:
- 复制延迟 > 30秒(普通业务)
- 复制延迟 > 5秒(核心业务)
- 主库不可连接 > 10秒
- 从库状态异常 > 5秒
3. 定期测试与演练
测试频率:至少每季度一次完整切换演练
测试场景:
- 主库进程崩溃(kill -9 postgres)
- 主库网络隔离(iptables屏蔽)
- 主库磁盘满(dd填充磁盘)
- 从库延迟切换
演练文档化:记录每次演练的步骤、结果和改进点
4. 数据一致性保障
根据业务选择复制类型:
- 核心业务:同步复制(synchronous_commit = on)
- 普通业务:半同步复制(synchronous_commit = remote_write)
- 非关键业务:异步复制(synchronous_commit = off)
WAL日志管理:
- 配置
wal_keep_size或使用复制槽 - 结合
pg_receivewal额外备份WAL - 定期验证WAL完整性
- 配置
5. 自动化工具优化
Patroni配置示例:
yamlscope: postgres-cluster name: pg1 restart_strategy: failover ttl: 30 retry_timeout: 10 maximum_lag_on_failover: 1048576 # 1MB延迟阈值repmgr配置示例:
inifailover=automatic promote_command='repmgr standby promote -f /etc/repmgr.conf --log-to-file' follow_command='repmgr standby follow -f /etc/repmgr.conf --log-to-file %n %d'
主从切换的常见问题与解决方案
1. 复制延迟导致的数据丢失
问题:主库故障时,从库存在较大复制延迟,切换后丢失最新数据
解决方案:
- 配置半同步或同步复制,确保关键数据已复制到从库
- 增大
wal_keep_size参数(PostgreSQL 13+)或使用复制槽 - 部署
pg_receivewal独立备份WAL日志 - 考虑使用
pg_rewind工具快速同步旧主库(适用于自动切换工具)
2. 脑裂问题
问题:网络分区导致多个节点同时认为自己是主库,造成数据分裂
解决方案:
- 使用奇数节点集群,实现多数派决策
- 配置
synchronous_standby_names要求写入确认 - 部署fencing机制(如STONITH、IPMI断电)
- 利用DCS(etcd/consul/zookeeper)实现分布式锁
- PostgreSQL 15+:使用
primary_conninfo的application_name确保唯一主库
3. 切换后应用连接失败
问题:切换完成后,应用程序无法连接到新主库
解决方案:
- 使用支持自动发现的连接池(如PgBouncer+Patroni)
- 配置DNS自动更新(如结合etcdctl更新DNS记录)
- 应用程序实现连接重试和拓扑感知
- 确保新主库
pg_hba.conf允许应用连接 - 检查新主库端口和防火墙配置
4. 从库提升失败
问题:执行提升操作时,从库无法成功转换为主库
解决方案:
- 检查从库状态:
pg_is_in_recovery()应为t - 确认WAL日志完整性:检查
pg_wal目录和日志文件 - 查看从库日志:
tail -n 100 /var/lib/pgsql/16/data/log/postgresql-*.log - 验证recovery配置:确保没有冲突的recovery参数
- 极端情况下,考虑使用
pg_resetwal工具(需谨慎,可能导致数据丢失)
5. 切换后性能下降
问题:切换到新主库后,数据库性能明显下降
解决方案:
- 确保新主库硬件配置与旧主库相当
- 验证新主库参数配置与旧主库一致
- 检查新主库的索引状态:
VACUUM ANALYZE优化 - 分析性能瓶颈:使用
pg_stat_statements和pg_stat_activity - 检查存储IO:使用
iostat确认磁盘性能
PostgreSQL 10-16 版本切换机制差异
| 版本 | 核心变化 | 切换相关特性 |
|---|---|---|
| PostgreSQL 10 | 引入逻辑复制 | 使用 recovery.conf,需手动创建promote文件无内置 pg_promote() 函数复制槽增强 |
| PostgreSQL 11 | 增强并行查询 | 保留 recovery.conf 机制改进复制槽管理 支持 pg_wal_replay_pause()/resume() |
| PostgreSQL 12 | 移除 recovery.conf | 引入 pg_promote() 函数复制配置移至 postgresql.auto.conf支持 recovery.signal 和 standby.signal 文件 |
| PostgreSQL 13 | 增强WAL管理 | 引入 wal_keep_size 参数改进 pg_stat_replication 视图支持 pg_rewind 自动清理 |
| PostgreSQL 14 | 增强高可用 | 引入 pg_wal_replay_wait_lsn() 函数改进同步复制行为 增强复制监控指标 |
| PostgreSQL 15 | 安全与性能增强 | 改进 pg_stat_replication 视图,增加更多指标增强复制槽可靠性 改进WAL发送机制 |
| PostgreSQL 16 | 并行与监控增强 | 改进复制性能 增强 pg_stat_replication 中的延迟指标支持更多自动切换工具集成 |
版本迁移注意事项
从 PostgreSQL 10-11 升级到 12+:
- 移除
recovery.conf,改用postgresql.auto.conf和 signal 文件 - 开始使用
pg_promote()函数替代手动创建promote文件
- 移除
从 PostgreSQL 12-13 升级到 14+:
- 使用
pg_wal_replay_wait_lsn()简化复制等待 - 利用增强的监控视图改进切换决策
- 使用
PostgreSQL 16 新特性:
- 更精确的复制延迟监控
- 改进的WAL发送机制,减少切换时间
- 更好的自动切换工具集成支持
