华为云openGauss高可用实战:主备复制+Keepalived实现RTO<10s
🎯 高可用架构设计
我们订单中心的 openGauss 集群架构如下:

架构说明:
| 角色 | 规格 | 部署位置 | 复制模式 | 用途 |
|---|---|---|---|---|
| 主库 | 鲲鹏 32C/64GB | 可用区 A | — | 读写 |
| 备库 1 | 鲲鹏 32C/64GB | 可用区 B | 同步 | 高可用 + 只读 |
| 备库 2 | 鲲鹏 16C/32GB | 可用区 C | 异步 | 灾备 + 只读 |
备库 1 用同步复制保证零数据丢失,备库 2 用异步复制做异地灾备。

🔧 第一步:搭建流式复制
主库配置
# 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 # 一切正常

🔄 第三步:主备切换流程
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

💥 踩坑:同步复制模式下备库升主库失败
现象:主库宕机后,备库的 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
💥 脑裂防御
高可用最怕的不是主库宕机,而是脑裂 —— 两个节点都认为自己是主库,都接受写入。

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';

🔄 日常切换演练
高可用方案不演练等于没有。我们每季度做一次主动切换演练。
#!/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 ✅ |

📊 复制延迟监控
-- 监控复制延迟
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

❓ 常见问题
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 内完成 ✅
下一篇文章讲异地容灾方案 —— 跨机房数据同步,应对区域性故障。
💬 互动:你们数据库的高可用方案是什么?发生过脑裂吗?怎么解决的?
- 点赞
- 收藏
- 关注作者
评论(0)