Skip to content

PostgreSQL 灾难恢复切换流程

灾难恢复类型

计划内切换

计划内切换是指在预先安排的维护窗口内进行的切换操作,如硬件升级、软件升级等。这种切换通常是可控的,有充分的准备时间。

计划外切换

计划外切换是指由于突发故障导致的切换操作,如主库宕机、网络中断、自然灾害等。这种切换需要快速响应,以减少业务中断时间。

灾难恢复架构

主备复制架构

主备复制是 PostgreSQL 最常用的灾难恢复架构,包括:

  • 物理复制:基于 WAL 日志的复制,主库和备库数据完全一致
  • 逻辑复制:基于逻辑变化的复制,可以选择性复制表或数据库

多活架构

多活架构允许多个数据库实例同时处理读写请求,提高系统的可用性和性能:

  • 共享存储多活:多个数据库实例共享同一份存储
  • 基于复制的多活:通过异步复制实现多活

异地灾备架构

异地灾备架构将备库部署在不同的地理区域,以应对区域性灾难:

  • 异步异地复制:主库和异地备库之间采用异步复制
  • 同步异地复制:主库和异地备库之间采用同步复制

计划内切换流程

1. 切换前准备

检查系统状态

sql
-- 检查主库状态
SELECT pg_is_in_recovery();  -- 主库返回 false
SELECT pg_stat_replication;   -- 查看备库连接状态

-- 检查备库状态
SELECT pg_is_in_recovery();  -- 备库返回 true
SELECT pg_last_wal_replay_lsn();  -- 查看备库回放位置
SELECT pg_last_wal_receive_lsn();  -- 查看备库接收位置

验证数据一致性

sql
-- 在主库上检查
SELECT pg_wal_lsn_diff(pg_current_wal_lsn(), pg_last_wal_replay_lsn()) AS replication_delay;

-- 在备库上检查
SELECT pg_last_wal_receive_lsn() = pg_last_wal_replay_lsn() AS is_caught_up;

备份关键配置

bash
# 备份主库配置文件
tar -czf postgres_config_backup.tar.gz /etc/postgresql/14/main/

# 备份主库 WAL 日志
pg_basebackup -h primary -U replicator -D /backup/wal_backup -X stream -F t -z

2. 执行切换操作

方法一:使用 pg_ctl 命令

bash
# 步骤1:停止主库写入
psql -h primary -U postgres -c "SELECT pg_wal_replay_pause();"

# 步骤2:等待备库追上主库
# 在备库上执行,直到返回 true
psql -h standby -U postgres -c "SELECT pg_last_wal_receive_lsn() = pg_last_wal_replay_lsn();"

# 步骤3:将备库提升为主库
pg_ctl -D /var/lib/postgresql/14/main promote

# 步骤4:更新应用连接配置
# 修改应用的数据库连接字符串,指向新的主库

# 步骤5:启动原主库作为新的备库
pg_ctl -D /var/lib/postgresql/14/main start
psql -h primary -U postgres -c "SELECT pg_basebackup -h standby -U replicator -D /var/lib/postgresql/14/main -X stream -F p -R;"

方法二:使用 Patroni 自动切换

bash
# 步骤1:查看当前集群状态
patronictl -c patroni.yml list

# 步骤2:执行切换操作
patronictl -c patroni.yml switchover

# 步骤3:验证切换结果
patronictl -c patroni.yml list

方法三:使用 repmgr 切换

bash
# 步骤1:查看当前集群状态
repmgr -f /etc/repmgr/14/repmgr.conf cluster show

# 步骤2:执行切换操作
repmgr -f /etc/repmgr/14/repmgr.conf standby promote

# 步骤3:重新注册原主库
repmgr -f /etc/repmgr/14/repmgr.conf node rejoin -f

# 步骤4:验证切换结果
repmgr -f /etc/repmgr/14/repmgr.conf cluster show

3. 切换后验证

检查新主库状态

sql
-- 检查是否为主库
SELECT pg_is_in_recovery();  -- 应返回 false

-- 检查连接数
SELECT count(*) FROM pg_stat_activity;

-- 检查复制状态
SELECT * FROM pg_stat_replication;

验证业务功能

bash
# 执行业务查询
psql -h new_primary -U app_user -d app_db -c "SELECT * FROM important_table LIMIT 10;"

# 执行数据修改操作
psql -h new_primary -U app_user -d app_db -c "INSERT INTO test_table VALUES (1, 'test');"

# 验证修改结果
psql -h new_primary -U app_user -d app_db -c "SELECT * FROM test_table WHERE id = 1;"

计划外切换流程

1. 故障检测

自动故障检测

大多数高可用解决方案都提供自动故障检测功能:

  • Patroni:通过 DCS(如 etcd、Consul)监控主库状态
  • repmgr:通过定期检查主库连接状态检测故障
  • Corosync/Pacemaker:通过心跳检测主库状态

手动故障检测

如果没有自动故障检测,需要手动检测主库状态:

bash
# 检查主库网络连接
ping primary_host

# 检查主库端口连接
telnet primary_host 5432

# 检查主库进程状态
ssh primary_host ps aux | grep postgres

2. 故障确认

确认主库确实发生故障,而不是临时网络问题:

bash
# 尝试连接主库
psql -h primary -U postgres -c "SELECT 1;"

# 检查主库日志
ssh primary_host tail -n 100 /var/log/postgresql/postgresql-14-main.log

# 检查系统日志
ssh primary_host tail -n 100 /var/log/syslog

3. 执行故障转移

方法一:使用 pg_ctl 命令

bash
# 步骤1:确认主库无法恢复
# 尝试重启主库,如果失败则执行故障转移
ssh primary_host pg_ctl -D /var/lib/postgresql/14/main start

# 步骤2:将备库提升为主库
pg_ctl -D /var/lib/postgresql/14/main promote

# 步骤3:更新应用连接配置
# 修改应用的数据库连接字符串,指向新的主库

# 步骤4:恢复原主库
# 修复原主库故障后,将其作为新的备库重新加入集群
pg_basebackup -h new_primary -U replicator -D /var/lib/postgresql/14/main -X stream -F p -R
pg_ctl -D /var/lib/postgresql/14/main start

方法二:使用 Patroni 自动故障转移

bash
# 步骤1:查看集群状态
patronictl -c patroni.yml list

# 步骤2:确认主库故障
# Patroni 通常会自动检测并执行故障转移

# 步骤3:验证故障转移结果
patronictl -c patroni.yml list

方法三:使用 repmgr 故障转移

bash
# 步骤1:查看集群状态
repmgr -f /etc/repmgr/14/repmgr.conf cluster show

# 步骤2:执行故障转移
repmgr -f /etc/repmgr/14/repmgr.conf standby promote -F

# 步骤3:重新注册原主库
repmgr -f /etc/repmgr/14/repmgr.conf node rejoin -f

# 步骤4:验证故障转移结果
repmgr -f /etc/repmgr/14/repmgr.conf cluster show

4. 故障转移后处理

数据完整性检查

sql
-- 检查数据一致性
SELECT count(*) FROM important_table;  -- 与故障前记录对比

-- 检查最近的事务
SELECT * FROM audit_table ORDER BY created_at DESC LIMIT 10;

-- 运行数据库完整性检查
VACUUM ANALYZE VERBOSE;

日志分析

bash
# 分析备库提升日志
tail -n 100 /var/log/postgresql/postgresql-14-main.log | grep -i "promote"

# 分析主库故障日志
ssh primary_host tail -n 200 /var/log/postgresql/postgresql-14-main.log

通知相关人员

  • 通知业务团队故障转移完成
  • 通知运维团队进行后续处理
  • 记录故障原因和处理过程

灾难恢复工具

Patroni

Patroni 是一个用于管理 PostgreSQL 高可用集群的工具,提供自动故障检测和故障转移功能。

主要特点

  • 自动故障检测和故障转移
  • 支持多种 DCS(etcd、Consul、ZooKeeper)
  • 支持动态配置管理
  • 支持 PostgreSQL 10+ 版本

配置示例

yaml
scope: postgres-cluster
namespace: /pg/
name: postgres-1

restapi:
  listen: 0.0.0.0:8008
  connect_address: 192.168.1.10:8008

etcd:
  hosts: 192.168.1.20:2379,192.168.1.21:2379,192.168.1.22: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
        max_wal_senders: 10
        max_replication_slots: 10
        wal_keep_size: 8GB

repmgr

repmgr 是一个用于管理 PostgreSQL 复制集群的工具,提供复制监控、故障转移和集群管理功能。

主要特点

  • 复制监控和状态报告
  • 手动和自动故障转移
  • 集群管理和维护
  • 支持 PostgreSQL 9.3+ 版本

配置示例

ini
# repmgr.conf
node_id=1
node_name=postgres-1
conninfo='host=192.168.1.10 user=repmgr dbname=repmgr connect_timeout=2'
data_directory='/var/lib/postgresql/14/main'
replication_user=replicator
replication_type=physical
monitoring_history=yes

Stolon

Stolon 是一个用于管理 PostgreSQL 集群的云原生解决方案,提供高可用性和自动故障转移功能。

主要特点

  • 云原生设计,支持 Kubernetes
  • 自动故障检测和故障转移
  • 支持水平扩展
  • 支持 PostgreSQL 9.5+ 版本

不同版本的灾难恢复特性

PostgreSQL 12 及以上版本

  • 引入了 pg_visibility 扩展,用于检查数据可见性
  • 改进了 pg_wal_replay_pause()pg_wal_replay_resume() 函数
  • 支持更大的 WAL 日志文件

PostgreSQL 13 及以上版本

  • 引入了增量排序功能,可能影响 WAL 日志大小
  • 改进了并行查询的 WAL 记录
  • 支持 wal_compression 参数,减少 WAL 日志大小

PostgreSQL 14 及以上版本

  • 引入了资源组功能,可用于限制特定用户的资源使用
  • 改进了 pg_rewind 功能,加快备库重建速度
  • 支持 log_recovery_conflict_waits 参数,用于记录恢复冲突等待

PostgreSQL 15 及以上版本

  • 引入了新的权限模型,影响复制配置
  • 改进了逻辑复制的性能和可靠性
  • 支持 pg_stat_wal 视图,提供更详细的 WAL 统计信息

常见问题(FAQ)

Q1: 如何选择合适的灾难恢复架构?

A1: 选择灾难恢复架构需要考虑以下因素:

  • RTO(恢复时间目标):允许的业务中断时间
  • RPO(恢复点目标):允许的数据丢失量
  • 成本预算:包括硬件、软件、人力成本
  • 业务重要性:不同业务对可用性的要求不同
  • 地理因素:是否需要异地灾备

Q2: 如何减少灾难恢复切换时间?

A2: 可以采取以下措施减少切换时间:

  • 使用自动故障检测和故障转移工具(如 Patroni)
  • 定期进行切换演练,熟悉切换流程
  • 优化备库配置,确保备库性能良好
  • 使用快速恢复技术(如 pg_rewind
  • 简化应用连接配置,便于快速切换

Q3: 如何验证灾难恢复的有效性?

A3: 可以通过以下方式验证灾难恢复的有效性:

  • 定期进行灾难恢复演练
  • 测试不同场景下的恢复时间
  • 验证数据一致性和完整性
  • 检查业务功能是否正常
  • 记录演练结果并持续改进

Q4: 如何处理主备库之间的复制延迟?

A4: 处理复制延迟可以采取以下措施:

  • 优化网络连接,减少网络延迟
  • 增加 WAL 发送和应用的并发度
  • 调整 max_wal_sendersmax_replication_slots 参数
  • 使用更快的存储设备
  • 考虑使用同步复制(但会影响主库性能)

Q5: 如何防止脑裂问题?

A5: 防止脑裂问题可以采取以下措施:

  • 使用 Quorum 机制,确保只有获得多数节点认可的节点才能成为主库
  • 配置合适的 maximum_lag_on_failover 参数
  • 使用 STONITH(Shoot The Other Node In The Head)技术
  • 定期检查集群状态,及时发现和处理脑裂

Q6: 如何恢复到指定时间点?

A6: 恢复到指定时间点可以使用以下方法:

bash
# 1. 停止备库
pg_ctl -D /var/lib/postgresql/14/main stop

# 2. 清理备库数据目录
rm -rf /var/lib/postgresql/14/main/*

# 3. 从基础备份恢复
pg_basebackup -h primary -U replicator -D /var/lib/postgresql/14/main -X stream -F p

# 4. 创建恢复配置文件
cat > /var/lib/postgresql/14/main/recovery.signal << EOF
restore_command = 'cp /path/to/wal/archive/%f %p'
recovery_target_time = '2023-01-01 10:00:00+08'
recovery_target_inclusive = true
EOF

# 5. 启动备库
pg_ctl -D /var/lib/postgresql/14/main start

# 6. 验证恢复结果
psql -h standby -U postgres -c "SELECT pg_is_in_recovery();"

Q7: 如何监控灾难恢复状态?

A7: 可以使用以下方法监控灾难恢复状态:

  • 使用 Patroni 或 repmgr 监控集群状态
  • 设置复制延迟告警
  • 监控备库接收和回放位置
  • 定期检查数据一致性
  • 使用监控工具(如 Prometheus + Grafana)可视化监控

Q8: 如何准备灾难恢复演练?

A8: 准备灾难恢复演练可以采取以下步骤:

  1. 制定演练计划,包括演练目标、范围、步骤和时间
  2. 通知相关人员,包括业务团队、运维团队和管理层
  3. 准备演练环境,确保与生产环境相似
  4. 备份生产数据,防止演练影响生产环境
  5. 执行演练,记录演练过程和结果
  6. 分析演练结果,找出改进点
  7. 更新灾难恢复计划和流程

灾难恢复最佳实践

1. 制定详细的灾难恢复计划

  • 明确灾难恢复目标(RTO、RPO)
  • 定义灾难恢复流程和责任分工
  • 定期更新和测试灾难恢复计划
  • 确保所有相关人员熟悉灾难恢复流程

2. 定期备份数据

  • 定期进行全量备份和增量备份
  • 备份到本地和异地存储
  • 定期验证备份的完整性和可用性
  • 测试从备份恢复的过程

3. 监控和告警

  • 监控主备库状态和复制延迟
  • 设置合适的告警阈值
  • 建立多级告警机制
  • 确保告警能够及时通知到相关人员

4. 定期演练

  • 至少每半年进行一次灾难恢复演练
  • 测试不同场景下的恢复过程
  • 记录演练结果,持续改进
  • 确保演练不会影响生产环境

5. 保持文档更新

  • 记录集群配置和架构
  • 记录灾难恢复流程和步骤
  • 记录历次故障和处理过程
  • 定期更新文档,确保与实际环境一致

配置验证和测试

1. 验证复制配置

sql
-- 检查主库复制配置
SELECT name, setting FROM pg_settings WHERE name IN (
  'wal_level', 'hot_standby', 'max_wal_senders', 
  'max_replication_slots', 'wal_keep_size'
);

-- 检查备库复制配置
SELECT name, setting FROM pg_settings WHERE name IN (
  'hot_standby', 'max_standby_streaming_delay', 
  'hot_standby_feedback'
);

2. 测试切换流程

bash
# 执行计划内切换演练
./disaster_recovery_switchover.sh

# 验证切换结果
psql -h new_primary -U postgres -c "SELECT pg_is_in_recovery();"

# 测试业务功能
./test_business_functions.sh

3. 测试故障转移

bash
# 模拟主库故障
ssh primary_host pkill -9 postgres

# 等待自动故障转移完成
sleep 30

# 验证故障转移结果
patronictl -c patroni.yml list

# 测试业务功能
./test_business_functions.sh

通过遵循本指南,可以建立完善的 PostgreSQL 灾难恢复切换流程,提高系统的可用性和可靠性,减少灾难对业务的影响。