Skip to content

PostgreSQL 异地灾备架构

核心概念

异地灾备架构是指将生产数据库的数据复制到地理位置上分离的灾备站点,以应对本地灾难导致的数据丢失或服务中断。主要目标包括:

  • 数据保护:确保关键数据在本地灾难发生时不丢失
  • 业务连续性:在灾难发生时能够快速恢复业务服务
  • 合规要求:满足行业法规对数据备份和灾难恢复的要求

异地灾备架构的关键要素:

  • 灾备站点:位于不同地理位置的备份站点
  • 复制技术:用于将数据从生产站点复制到灾备站点的技术
  • 网络连接:连接生产站点和灾备站点的网络基础设施
  • 恢复流程:灾难发生时的恢复步骤和流程
  • 监控告警:对复制状态和灾备站点的监控和告警

异地灾备架构设计

1. 基于距离的架构设计

距离类型距离范围网络延迟适用架构
同城异地< 50km< 10ms同步复制 + 异步复制
跨城异地50-500km10-100ms异步复制 + WAL归档
跨省异地> 500km> 100ms异步复制 + 定期基础备份

2. 基于RPO/RTO的架构设计

RPO要求RTO要求推荐架构
< 5分钟< 30分钟同步复制 + 自动故障切换
< 1小时< 4小时异步复制 + WAL归档
< 24小时< 8小时定期基础备份 + WAL归档

3. 常见异地灾备架构

3.1 主从复制架构

最常用的异地灾备架构,使用PostgreSQL内置的流复制功能:

生产站点(主库) → 异地灾备站点(从库)
  ↓                    ↓
应用连接            只读查询/故障切换

3.2 级联复制架构

适用于远距离灾备,减少主库的复制压力:

生产站点(主库) → 本地从库 → 异地灾备站点(级联从库)

应用连接

3.3 双主复制架构

适用于需要双向复制的场景,支持双活或主备切换:

生产站点(主库1) ↔ 异地灾备站点(主库2)
  ↓                    ↓
应用连接            应用连接(可选)

3.4 混合云架构

结合公有云和私有云的灾备架构:

私有云(生产) → 公有云(灾备)
  ↓                    ↓
应用连接            故障切换目标

异地灾备实现方法

1. 基于流复制的异地灾备

这是最常用的异地灾备实现方法,使用PostgreSQL内置的流复制功能:

配置步骤

bash
# 1. 配置生产库(主库)
# 修改postgresql.conf
cat >> /var/lib/postgresql/15/main/postgresql.conf << EOF
# 流复制配置
wal_level = replica
max_wal_senders = 5
wal_keep_size = 1GB
max_replication_slots = 5
EOF

# 修改pg_hba.conf,允许灾备库连接
cat >> /var/lib/postgresql/15/main/pg_hba.conf << EOF
# 允许灾备库的复制连接
host replication replication_user 灾备库IP/32 md5
EOF

# 重启主库使配置生效
pg_ctl restart -D /var/lib/postgresql/15/main

# 2. 配置灾备库(从库)
# 清理数据目录
rm -rf /var/lib/postgresql/15/main/*

# 从主库创建基础备份
pg_basebackup -h 主库IP -U replication_user -D /var/lib/postgresql/15/main -F p -X stream -R

# 修改postgresql.conf(可选,根据需要调整)
cat >> /var/lib/postgresql/15/main/postgresql.conf << EOF
# 灾备库配置
hot_standby = on
max_standby_streaming_delay = 30s
hot_standby_feedback = on
EOF

# 启动灾备库
pg_ctl start -D /var/lib/postgresql/15/main

# 3. 验证复制状态
# 在主库上查看
psql -h 主库IP -U postgres -c "SELECT * FROM pg_stat_replication;"

# 在从库上查看
psql -h 灾备库IP -U postgres -c "SELECT * FROM pg_stat_wal_receiver;"
psql -h 灾备库IP -U postgres -c "SELECT pg_is_in_recovery();"

2. 基于WAL归档的异地灾备

适用于网络条件较差的场景,通过WAL文件归档实现异地灾备:

配置步骤

bash
# 1. 配置生产库(主库)的WAL归档
cat >> /var/lib/postgresql/15/main/postgresql.conf << EOF
# WAL归档配置
archive_mode = on
archive_command = 'rsync -a %p backup_user@灾备库IP:/pg_backups/wal/%f'
archive_timeout = 300
EOF

# 重启主库使配置生效
pg_ctl restart -D /var/lib/postgresql/15/main

# 2. 定期创建基础备份
# 使用pg_basebackup定期创建基础备份
pg_basebackup -h 主库IP -U replication_user -D /pg_backups/base/$(date +%Y%m%d_%H%M%S) -F t -z

# 3. 配置灾备库
# 恢复基础备份
tar -xzf /pg_backups/base/20230101_120000.tar.gz -C /var/lib/postgresql/15/main/

# 配置恢复参数
cat > /var/lib/postgresql/15/main/recovery.signal << EOF
restore_command = 'cp /pg_backups/wal/%f %p'
recovery_target_timeline = 'latest'
hot_standby = on
EOF

# 启动灾备库
pg_ctl start -D /var/lib/postgresql/15/main

3. 基于第三方工具的异地灾备

3.1 使用pg_probackup实现异地灾备

bash
# 1. 在生产库上安装pg_probackup
# 略

# 2. 初始化备份目录
pg_probackup init -B /pg_backups

# 3. 注册实例
pg_probackup add-instance -B /pg_backups -D /var/lib/postgresql/15/main -i postgres

# 4. 创建基础备份
pg_probackup backup -B /pg_backups -i postgres -b full -h localhost -p 5432 -U postgres

# 5. 将备份复制到灾备库
rsync -a /pg_backups/ backup_user@灾备库IP:/pg_backups/

# 6. 在灾备库上恢复备份
pg_probackup restore -B /pg_backups -i postgres -D /var/lib/postgresql/15/main --recovery-target-time='2023-01-01 12:00:00'

3.2 使用Barman实现异地灾备

bash
# 1. 在Barman服务器上配置postgresql.conf
# 略

# 2. 配置Barman
cat >> /etc/barman.conf << EOF
[barman]
barman_home = /var/lib/barman
barman_user = barman
log_file = /var/log/barman/barman.log
compression = gzip
retention_policy = RECOVERY WINDOW OF 7 DAYS

[postgres]
host = 主库IP
user = barman
dbname = postgres
backup_method = postgres
streaming_conninfo = host=主库IP user=streaming_barman
EOF

# 3. 执行备份
barman backup postgres

# 4. 从Barman恢复到灾备库
barman recover postgres latest /var/lib/postgresql/15/main --remote-ssh-command "ssh postgres@灾备库IP"

异地灾备监控与管理

1. 复制状态监控

bash
# 1. 监控主库复制状态
psql -h 主库IP -U postgres -c "SELECT * FROM pg_stat_replication;"

# 2. 监控从库复制状态
psql -h 灾备库IP -U postgres -c "SELECT * FROM pg_stat_wal_receiver;"
psql -h 灾备库IP -U postgres -c "SELECT * FROM pg_stat_replication;"

# 3. 监控复制延迟
psql -h 灾备库IP -U postgres -c "SELECT now() - pg_last_xact_replay_timestamp() AS replication_delay;"

# 4. 使用pg_stat_monitor监控(PostgreSQL 14+)
psql -h 主库IP -U postgres -c "SELECT * FROM pg_stat_monitor.replication;"

2. 自动告警配置

bash
# 1. 使用Prometheus + Grafana监控
# 安装pg_exporter
# 配置Prometheus抓取配置
# 配置Grafana仪表盘

# 2. 配置告警规则
# 在Prometheus中配置告警规则
cat >> /etc/prometheus/alert_rules.yml << EOF
groups:
- name: postgresql_replication
  rules:
  - alert: ReplicationLagHigh
    expr: pg_stat_replication_replay_lag_seconds > 300
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "PostgreSQL复制延迟过高"
      description: "复制延迟超过300秒"

  - alert: ReplicationDown
    expr: pg_stat_replication_count == 0
    for: 5m
    labels:
      severity: critical
    annotations:
      summary: "PostgreSQL复制中断"
      description: "复制连接已中断5分钟"
EOF

3. 定期演练与测试

bash
# 1. 定期执行恢复测试
# 停止灾备库
pg_ctl stop -D /var/lib/postgresql/15/main

# 清理数据目录
rm -rf /var/lib/postgresql/15/main/*

# 恢复最新备份
pg_basebackup -h 主库IP -U replication_user -D /var/lib/postgresql/15/main -F p -X stream -R

# 启动灾备库
pg_ctl start -D /var/lib/postgresql/15/main

# 验证恢复结果
psql -h 灾备库IP -U postgres -c "SELECT * FROM critical_table LIMIT 10;"

# 2. 定期测试故障切换
# 在灾备库上执行故障切换
pg_ctl promote -D /var/lib/postgresql/15/main

# 验证切换结果
psql -h 灾备库IP -U postgres -c "SELECT pg_is_in_recovery();"  # 应返回f

# 切换回主库(可选)
# 略

最佳实践

1. 架构设计最佳实践

  • 选择合适的复制方式:根据网络条件和RPO/RTO要求选择同步复制或异步复制
  • 考虑网络带宽:确保生产站点和灾备站点之间有足够的网络带宽
  • 设计多层灾备:结合本地备份和异地灾备,构建多层次的灾备体系
  • 考虑存储差异:确保灾备站点的存储性能和容量满足恢复要求

2. 配置最佳实践

bash
# 优化主库配置
cat >> /var/lib/postgresql/15/main/postgresql.conf << EOF
# 复制优化配置
wal_compression = on
max_wal_senders = 10
wal_keep_size = 2GB
max_replication_slots = 10
# 减少WAL生成量
archive_timeout = 60
checkpoint_timeout = 30min
max_wal_size = 4GB
EOF

# 优化从库配置
cat >> /var/lib/postgresql/15/main/postgresql.conf << EOF
# 从库优化配置
hot_standby = on
max_standby_streaming_delay = 60s
hot_standby_feedback = on
# 并行恢复
max_worker_processes = 8
max_parallel_workers_per_gather = 4
EOF

3. 管理最佳实践

  • 文档化架构和流程:详细记录异地灾备架构、配置和恢复流程
  • 定期更新备份:定期执行基础备份,减少恢复时需要应用的WAL日志数量
  • 测试恢复流程:至少每季度测试一次完整的恢复流程
  • 培训团队:确保运维团队熟悉异地灾备架构和恢复流程
  • 定期审查和更新:根据业务需求和技术发展,定期审查和更新异地灾备架构

常见问题(FAQ)

Q1:如何选择合适的异地灾备架构?

A1:选择异地灾备架构时需要考虑以下因素:

  • 业务重要性:关键业务需要更高级别的灾备保护
  • RPO/RTO要求:根据恢复点目标和恢复时间目标选择合适的架构
  • 网络条件:网络延迟和带宽影响复制方式的选择
  • 成本预算:不同架构的成本差异较大
  • 合规要求:满足行业法规对数据备份和灾难恢复的要求

Q2:如何处理异地复制延迟问题?

A2:

bash
# 1. 优化网络连接
# 增加网络带宽
# 优化网络路由
# 使用专用网络连接

# 2. 优化数据库配置
# 减少WAL生成量
cat >> /var/lib/postgresql/15/main/postgresql.conf << EOF
wal_compression = on
checkpoint_timeout = 30min
max_wal_size = 4GB
archive_timeout = 60
EOF

# 3. 优化复制配置
# 增加从库资源
# 启用并行恢复
cat >> /var/lib/postgresql/15/main/postgresql.conf << EOF
max_worker_processes = 8
max_parallel_workers_per_gather = 4
EOF

# 4. 监控复制延迟
# 使用Prometheus + Grafana监控复制延迟
# 配置告警规则

Q3:如何实现自动故障切换?

A3:

bash
# 1. 使用Patroni实现自动故障切换
# 安装Patroni
pip install patroni[postgresql]

# 配置Patroni
cat > /etc/patroni/patroni.yml << EOF
scope: postgres
namespace: /db/
name: pg01

restapi:
  listen: 主库IP:8008
  connect_address: 主库IP:8008

etcd:
  host: etcd_host:2379

ttl: 30
loop_wait: 10
retry_timeout: 10

postgresql:
  listen: 主库IP:5432
  connect_address: 主库IP:5432
  data_dir: /var/lib/postgresql/15/main
  bin_dir: /usr/lib/postgresql/15/bin
  pgpass: /tmp/pgpass0
  authentication:
    replication:
      username: replication
      password: rep_pass
    superuser:
      username: postgres
      password: postgres_pass
    rewind:
      username: rewind_user
      password: rewind_pass
  parameters:
    unix_socket_directories: /var/run/postgresql
    wal_level: replica
    hot_standby: on
    logging_collector: on
    log_directory: /var/log/postgresql
    log_filename: postgresql-%Y-%m-%d_%H%M%S.log
    log_statement: all
EOF

# 启动Patroni
patroni /etc/patroni/patroni.yml

Q4:如何验证异地灾备的有效性?

A4:

  1. 定期恢复测试:定期从异地备份恢复数据库,验证数据完整性
  2. 复制状态监控:实时监控复制状态和延迟
  3. 故障切换测试:定期测试故障切换流程,确保能够快速恢复服务
  4. 数据一致性验证:定期验证生产库和灾备库的数据一致性
  5. 文档审查:定期审查灾备文档和流程,确保其准确性和完整性

Q5:如何处理异地灾备中的数据冲突?

A5:

  • 预防为主:设计架构时避免双写场景
  • 使用逻辑复制:逻辑复制可以更灵活地处理数据冲突
  • 实现冲突检测和解决机制
    sql
    -- 使用触发器检测冲突
    CREATE OR REPLACE FUNCTION detect_conflict()
    RETURNS trigger AS $$
    BEGIN
      IF EXISTS (SELECT 1 FROM mytable WHERE id = NEW.id) THEN
        -- 处理冲突,例如记录日志或使用特定策略解决
        INSERT INTO conflict_log (table_name, conflict_data) 
        VALUES ('mytable', row_to_json(NEW));
        RETURN NULL;
      END IF;
      RETURN NEW;
    END;
    $$ LANGUAGE plpgsql;
    
    CREATE TRIGGER mytable_conflict_trigger
    BEFORE INSERT ON mytable
    FOR EACH ROW
    EXECUTE FUNCTION detect_conflict();

Q6:如何降低异地灾备的成本?

A6:

  • 使用增量备份:减少备份数据量和存储成本
  • 优化存储层级:使用不同层级的存储,将近期备份放在高性能存储,远期备份放在低成本存储
  • 使用公有云:利用公有云的弹性存储和按需付费模型
  • 共享灾备资源:多个应用共享一个灾备站点
  • 优化复制策略:根据数据重要性采用不同的复制策略