华为云openGauss高可用实战:主备复制+Keepalived实现RTO<10s

举报
行者·全栈架构师 发表于 2026/07/18 14:46:17 2026/07/18
【摘要】 生产环境数据库挂了怎么办?openGauss 的主备流式复制 + Keepalived VIP 漂移方案可以做到故障自动切换、RTO < 10 秒。本文详细讲解了主备架构搭建、同步/异步模式选型、Keepalived 配置、脑裂防御、日常切换演练等核心内容。所有配置均来自线上生产环境,可直接复用。

🎯 高可用架构设计

我们订单中心的 openGauss 集群架构如下:

HWG-007-opengauss-ha-keepalived_diagram_1.png

架构说明

角色 规格 部署位置 复制模式 用途
主库 鲲鹏 32C/64GB 可用区 A 读写
备库 1 鲲鹏 32C/64GB 可用区 B 同步 高可用 + 只读
备库 2 鲲鹏 16C/32GB 可用区 C 异步 灾备 + 只读

备库 1 用同步复制保证零数据丢失,备库 2 用异步复制做异地灾备

HWG-007-opengauss-ha-keepalived_drill-flow.png

🔧 第一步:搭建流式复制

主库配置

# postgresql.conf — 主库
wal_level = logical              # 必须设为 logical 或 hot_standby
max_wal_senders = 10             # 最大 WAL 发送进程数(>= 备库数量)
wal_keep_segments = 1024         # 保留的 WAL 段数量(约 16GB)
max_replication_slots = 10       # 复制槽位数
synchronous_standby_names = 'standby1'  # 同步备库的名称
# pg_hba.conf — 允许备库连接复制
# TYPE  DATABASE  USER     ADDRESS           METHOD
host    replication omm     192.168.100.20/32 trust
host    replication omm     192.168.100.30/32 trust
# 重载配置
gs_guc reload -D $PGDATA

备库配置

# 用 gs_ctl 构建备库(替代 PostgreSQL 的 pg_basebackup)
gs_ctl build -D /data/opengauss/standby -M standby \
    -h 192.168.100.10 -p 5432 -U omm

# 备库 postgresql.conf
port = 5432
hot_standby = on                 # 允许只读查询
wal_level = logical
max_wal_senders = 10

验证复制状态

-- 主库查看复制状态
SELECT * FROM pg_stat_replication;

-- 输出示例
--  pid  | application_name | state | sync_state | write_lag | flush_lag | replay_lag
-- ------+------------------+-------+------------+-----------+-----------+-----------
-- 12345 | standby1         | streaming | sync     | 0ms       | 0ms       | 0ms
-- 12346 | standby2         | streaming | async    | 12ms      | 15ms      | 18ms

🔄 第二步:Keepalived 配置

Keepalived 负责监控主库健康状态,故障时自动将 VIP 漂移到备库。

主库配置(192.168.100.10)

# /etc/keepalived/keepalived.conf
global_defs {
    router_id OG_MASTER_1
}

vrrp_script check_og_alive {
    script "/opt/scripts/check_og_master.sh"
    interval 2          # 每 2 秒检测一次
    timeout 3           # 超时 3 秒
    fall 2              # 连续 2 次失败判为宕机
    rise 2              # 连续 2 次成功判为恢复
}

vrrp_instance VI_OG {
    state MASTER
    interface eth0
    virtual_router_id 100
    priority 150               # 主库优先
    advert_int 1               # VRRP 广播间隔(秒)
    
    authentication {
        auth_type PASS
        auth_prefix opengauss_ha
    }
    
    virtual_ipaddress {
        192.168.100.100/24 dev eth0  # VIP
    }
    
    track_script {
        check_og_alive
    }
    
    notify_master "/opt/scripts/switch_to_master.sh"
    notify_backup "/opt/scripts/switch_to_backup.sh"
    notify_fault "/opt/scripts/switch_to_fault.sh"
}

备库配置(192.168.100.20)

# /etc/keepalived/keepalived.conf — 备库只有 priority 不同
global_defs {
    router_id OG_STANDBY_1
}

vrrp_script check_og_alive {
    script "/opt/scripts/check_og_standby.sh"
    interval 2
    timeout 3
    fall 2
    rise 2
}

vrrp_instance VI_OG {
    state BACKUP
    interface eth0
    virtual_router_id 100
    priority 100                # 备库优先级低于主库
    advert_int 1
    
    authentication {
        auth_type PASS
        auth_prefix opengauss_ha
    }
    
    virtual_ipaddress {
        192.168.100.100/24 dev eth0
    }
    
    track_script {
        check_og_alive
    }
    
    notify_master "/opt/scripts/switch_to_master.sh"
    notify_backup "/opt/scripts/switch_to_backup.sh"
    notify_fault "/opt/scripts/switch_to_fault.sh"
}

健康检测脚本

#!/bin/bash
# /opt/scripts/check_og_master.sh — 主库健康检测

PGHOST="127.0.0.1"
PGPORT="5432"
PGUSER="omm"
PGPASSWORD="omm_pass"

# 1. 检查进程是否存活
pg_isready -h $PGHOST -p $PGPORT -U $PGUSER > /dev/null 2>&1
if [ $? -ne 0 ]; then
    exit 1  # 触发 VIP 漂移
fi

# 2. 检查是否能执行查询
gsql -h $PGHOST -p $PGPORT -U $PGUSER -d postgres \
    -t -c "SELECT 1;" > /dev/null 2>&1
if [ $? -ne 0 ]; then
    exit 1  # 数据库僵死
fi

# 3. 检查复制是否正常(备库升主库前确保没有同步问题)
LAG=$(gsql -h $PGHOST -p $PGPORT -U $PGUSER -d postgres \
    -t -c "SELECT COALESCE(MAX(pg_wal_lsn_diff(write_lag, '0/0')::text)::BIGINT, 0) 
           FROM pg_stat_replication;" 2>/dev/null | tr -d ' ')
           
if [ "$LAG" -gt 1073741824 ]; then  # 延迟超过 1GB
    exit 1  # 复制延迟太大,触发切换
fi

exit 0  # 一切正常

HWG-007-opengauss-ha-keepalived_vip-failover.png

🔄 第三步:主备切换流程

Keepalived 检测到主库故障后,自动执行以下流程:

#!/bin/bash
# /opt/scripts/switch_to_master.sh — 备库升主库

MASTER_HOST="192.168.100.20"  # 本机
PROMOTE_LOG="/var/log/og_promote.log"

echo "[$(date)] 开始提升为主库..." >> $PROMOTE_LOG

# 1. 停止 Keepalived(避免 VIP 争夺)
systemctl stop keepalived

# 2. 提升为可写主库
gs_ctl promote -D /data/opengauss/standby
if [ $? -ne 0 ]; then
    echo "[ERROR] promote 失败" >> $PROMOTE_LOG
    exit 1
fi

# 3. 修改配置为 writeable
gs_guc set -D /data/opengauss/standby \
    -c "synchronous_standby_names = ''"  # 临时取消同步复制依赖

# 4. 启动 Keepalived(VIP 漂移到本机)
systemctl start keepalived

# 5. 发送告警
curl -X POST http://alert.company.com/api/alert \
    -H "Content-Type: application/json" \
    -d '{"level":"critical","message":"openGauss 主备切换完成,新主库: 192.168.100.20"}'

echo "[$(date)] 提升完成,新主库已接管" >> $PROMOTE_LOG

HWG-007-opengauss-ha-keepalived_failover-sequence.png

💥 踩坑:同步复制模式下备库升主库失败

现象:主库宕机后,备库的 gs_ctl promote 命令在同步复制模式下卡住了。

原因synchronous_standby_names = 'standby1' 配置下,主库要求至少有一个同步备库确认 WAL。备库在升主时也会尝试连接同步备库(虽然此时没有备库可连),导致 promote 无法完成。

解决

# promote 前先去掉同步复制配置
gs_guc set -D /data/opengauss/standby \
    -c "synchronous_standby_names = ''"

# 然后再 promote
gs_ctl promote -D /data/opengauss/standby

💥 脑裂防御

高可用最怕的不是主库宕机,而是脑裂 —— 两个节点都认为自己是主库,都接受写入。

HWG-007-opengauss-ha-keepalived_diagram_2.png

STONITH 方案

我们采用了 STONITH(Shoot The Other Node In The Head) 思想:如果备库决定升主,必须确认旧主库已下线。

#!/bin/bash
# /opt/scripts/stonith.sh — 升主前强制关闭旧主库

OLD_MASTER_IP="192.168.100.10"
BASTION_HOST="192.168.100.200"  # 带外管理服务器

# 方式一:通过 iDRAC/BMC 远程关机(推荐)
ssh $BASTION_HOST "ipmitool -H ${OLD_MASTER_IP}_bmc -U admin chassis power off"

# 方式二:SSH 关闭(当主库还有响应时)
ssh $OLD_MASTER_IP "systemctl stop opengauss" 2>/dev/null

# 等待确认
for i in $(seq 1 10); do
    ping -c 1 -W 1 $OLD_MASTER_IP > /dev/null 2>&1
    if [ $? -ne 0 ]; then
        echo "[OK] 旧主库已离线"
        exit 0
    fi
    sleep 1
done

# 如果 10 秒后还能 ping 通,强制切断
ssh $BASTION_HOST "ipmitool -H ${OLD_MASTER_IP}_bmc chassis power cycle"

半同步 + 超时方案

对于网络分区(而非主机宕机)场景,我们设置了半同步超时:

# postgresql.conf
synchronous_commit = remote_write  # 备库写到 OS 层即可,不用 flush
synchronous_standby_names = 'ANY 1 (standby1)'  # 任一备库确认即可

当同步备库超过 30 秒没有响应,自动降级为异步:

-- 可以通过函数监控同步状态
SELECT CASE 
    WHEN sync_state = 'sync' AND NOT pg_is_in_recovery() 
    THEN 'healthy' 
    ELSE 'degraded' 
END AS replication_status
FROM pg_stat_replication 
WHERE application_name = 'standby1';

HWG-007-opengauss-ha-keepalived_stonith-flow.png

🔄 日常切换演练

高可用方案不演练等于没有。我们每季度做一次主动切换演练。

#!/bin/bash
# /opt/scripts/ha_drill.sh — 主动切换演练

echo "=== openGauss 高可用演练开始 ==="
echo "时间: $(date)"

# 1. 记录切换前状态
echo "[STEP 1/5] 记录当前主库..."
gsql -h 192.168.100.100 -U omm -d postgres \
    -t -c "SELECT inet_server_addr()" > /tmp/ha_before.txt

# 2. 通知业务降级
echo "[STEP 2/5] 通知业务降级..."
curl -X POST http://alert.company.com/api/ha_warning

# 3. 停止主库 Keepalived(触发 VIP 漂移)
echo "[STEP 3/5] 停止主库 Keepalived..."
ssh 192.168.100.10 "systemctl stop keepalived"

# 4. 等待切换完成并验证
echo "[STEP 4/5] 等待切换并验证..."
sleep 10

NEW_MASTER=$(gsql -h 192.168.100.100 -U omm -d postgres \
    -t -c "SELECT inet_server_addr()" 2>/dev/null | tr -d ' ')
echo "新主库 IP: $NEW_MASTER"

# 5. 恢复原主库为备库
echo "[STEP 5/5] 恢复原主库为备库..."
ssh 192.168.100.10 "
    systemctl start keepalived
    gs_ctl build -D /data/opengauss/data -M standby \
        -h $NEW_MASTER -p 5432 -U omm
"

echo "=== 演练完成 ==="
echo "切换时间: $((END_TIME - START_TIME)) 秒"

演练结束后要验证的指标:

指标 目标 实际
VIP 漂移时间 < 5s 2.3s ✅
promote 完成时间 < 3s 1.1s ✅
应用可恢复时间 < 10s 6.5s ✅
数据丢失量 0(同步模式) 0 ✅
回切时间 < 30s 18s ✅

HWG-007-opengauss-ha-keepalived_drill-flow.png

📊 复制延迟监控

-- 监控复制延迟
SELECT 
    application_name,
    state,
    sync_state,
    pg_wal_lsn_diff(pg_current_wal_lsn(), write_lag) AS write_bytes_behind,
    pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lag) AS replay_bytes_behind,
    CASE 
        WHEN write_lag IS NULL THEN 'disconnected'
        ELSE 'ok'
    END AS status
FROM pg_stat_replication;

设置告警阈值:

#!/bin/bash
# /opt/scripts/check_replication_lag.sh

MAX_LAG_BYTES=1073741824  # 1GB

LAG=$(gsql -h localhost -U omm -d postgres -t -c \
    "SELECT COALESCE(MAX(pg_wal_lsn_diff(write_lag, '0/0')::text)::BIGINT, 0) 
     FROM pg_stat_replication;" | tr -d ' ')

if [ "$LAG" -gt "$MAX_LAG_BYTES" ]; then
    echo "[CRITICAL] 复制延迟超过 1GB: ${LAG} bytes"
    # 触发告警
fi

HWG-007-opengauss-ha-keepalived_lag-monitoring.png

❓ 常见问题

Q1:同步复制模式下,备库宕机会影响主库吗?

。如果 synchronous_standby_names 配了特定的备库名,该备库宕机后主库的事务提交会等待超时(synchronous_commit = remote_write 下等待 wal_receiver_timeout 参数设置的时间,默认 60 秒)。建议使用 ANY 1 (standby1, standby2) 语法,任一备库确认即可。

Q2:主备切换后,原来的主库怎么恢复?

原主库恢复后,不能直接重新加入集群。需要重新拉取数据成为新主库的备库:

# 在原主库上执行
gs_ctl build -D /data/opengauss/data -M standby \
    -h <新主库IP> -p 5432 -U omm

Q3:网络抖动会导致频繁切换吗?

Keepalived 的配置里 fall 2 + interval 2 意味着连续 4 秒检测失败才判为宕机,短时网络抖动不会触发切换。如果网络真的不稳定,建议加到 fall 3(6 秒)。

Q4:应用端需要做什么特殊配置吗?

应用连接使用 VIP(192.168.100.100)即可,不需要感知后端主备切换。但建议连接池配置自动重连

# HikariCP 配置
spring.datasource.hikari.connection-timeout=5000
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.idle-timeout=600000

✅ 高可用方案检查清单

【部署阶段】
□ 主库 wal_level = logical
□ 备库 hot_standby = on
□ 复制状态验证(pg_stat_replication 流式状态)
□ Keepalived 主备 priority 已区分
□ VIP 漂移验证(停止主库后 VIP 是否自动漂移)
□ 健康检测脚本覆盖进程检测 + SQL 检测 + 复制检测

【运维阶段】
□ 复制延迟监控已配置告警
□ STONITH 方案(强制关闭旧主库)
□ 季度切换演练排期
□ 同步模式下备库宕机应急方案(降级为异步)
□ 主库恢复后 rejoin 脚本已就绪

📝 总结

最好的高可用方案不是永远不会出故障,而是出了故障你睡得着觉

这套 Keepalived + 流式复制的方案,我们运行了一年多,经历了 3 次真实故障和 4 次主动演练:

  • 硬件故障 2 次:磁盘坏道 → 自动切换 → RTO 6s ✅
  • 网络抖动 1 次:触发切换 → 发现问题后优化了 fall 参数
  • 演练切换 4 次:全部在 10s 内完成 ✅

下一篇文章讲异地容灾方案 —— 跨机房数据同步,应对区域性故障。

💬 互动:你们数据库的高可用方案是什么?发生过脑裂吗?怎么解决的?

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。