华为云openGauss异地容灾:跨机房复制+OBS归档实战方案
📝 文章摘要:2025 年 6 月,我们机房因光纤被施工队挖断,整个可用区离线了 47 分钟。幸亏做了异地容灾,业务在 3 分钟内切换到了异地备库。本文详细记录了这套基于 openGauss + 华为云 OBS / DNS / KMS / CES / SMN 的跨机房容灾方案:异步流式复制如何应对网络延迟、OBS 归档如何做异地备份、以及 4 次容灾演练中我们发现的 5 个致命问题。如果你正在建设数据库容灾体系,这些经验能帮你省下至少两个月的试错时间。
⏱ 预计阅读时间:20 分钟
🎯 背景:光纤被挖断那天
2025 年 6 月 13 日下午 2:17,我的手机突然被告警轰炸。
“数据库主库连接超时!”
“订单创建接口 100% 失败!”
“Keepalived VIP 漂移失败!”
第一反应:主库挂了?登录跳板机一看,主库服务器还能 ping 通,但通过 gsql 连接时卡住不动。再查,发现主库所在可用区的网络整体中断 —— 后来运营商反馈,附近工地施工挖断了光纤。
主库挂了,备库在同一个可用区的另一个机柜里,也连不上。这就是同机房部署的致命问题 —— 单点故障不只是服务器,还有网络。
那次事故让我们 RTO 长达 47 分钟(等网络恢复后手动拉起服务)。事后复盘,管理层只问了一句话:“如果再发生一次,你能保证 5 分钟内恢复吗?”
于是异地容灾项目正式立项,技术栈全部基于华为云信创生态:openGauss 数据库 + 鲲鹏服务器 + OBS 对象存储 + 华为云 DNS。
🏗️ 容灾架构设计
整体架构采用"同城双活 + 异地灾备 + OBS 归档"三层防御,全部部署在华为云信创资源池上。

核心设计决策:
| 决策 | 选择 | 原因 |
|---|---|---|
| 复制模式 | 异步 | 跨机房 80km,光纤延迟约 2ms,同步模式会阻塞主库事务 |
| 切换方式 | 手动 + 脚本辅助 | 自动切换风险太高(误判网络抖动导致脑裂),接受 5-10 分钟 RTO |
| 距离 | 80km | 同城双活标准距离,兼顾延迟和地理容灾 |
| 备份 | 华为云 OBS + WAL 归档(KMS 服务端加密) | 对象存储跨可用区冗余,即使两个机房都挂了也有数据 |
| 监控告警 | CES 云监控 + SMN 消息通知 | 原生服务联动,告警到达手机时延小于 5s |
🇨🇳 国产化适配:为什么选择 openGauss + 鲲鹏
容灾项目立项时,技术选型有两个方向:沿用 MySQL,或者切换到信创栈。最终选择 openGauss 的原因:
| 维度 | openGauss | MySQL 8.0 | 说明 |
|---|---|---|---|
| 知识产权 | 完全自主 | Oracle 控制 | 信创合规要求 |
| 鲲鹏优化 | 原生深优,多核性能提升 30%+ | 通用编译,性能一般 | 与华为云鲲鹏实例深度协同 |
| 国密算法 | 原生支持 SM2/SM3/SM4 | 不支持 | 等保 2.0 三级要求 |
| 主从延迟 | 小于 10ms | 小于 50ms | 容灾场景关键指标 |
| 生态兼容 | 兼容 PostgreSQL | 自有生态 | 应用改造成本可控 |
| 华为官方支持 | 7x24 小时 | 社区支持 | 故障响应及时 |
主备库全部部署在鲲鹏 KC1 实例上,操作系统统一为 openEuler 22.03 LTS,编译器使用毕昇 JDK(应用层)+ GCC 10.3(数据库层)。这是华为云信创推荐的三件套。
🔧 第一步:搭建异步流式复制
配置跨机房复制
# 主库 postgresql.conf(跨机房专属配置)
wal_level = logical
max_wal_senders = 10
wal_keep_segments = 2048 # 增加 WAL 保留量,应对网络中断
max_replication_slots = 10
synchronous_commit = remote_write # 主库不等待备库确认
wal_sender_timeout = 60000 # WAL 发送超时 60s(默认 30s)
网络延时会导致复制延迟,我们需要在数据安全和主库性能之间做取舍:
# 主库 postgresql.conf — 延迟优化
# 增大 WAL 发送缓冲区,减少网络往返
wal_sender_batch_size = 64MB # 默认 8MB,增大后减少 TCP 小包
wal_compression = on # 跨机房链路必备,压缩比约 3:1
跨网段复制 + 安全组配置
跨机房走的是华为云 VPC 之间的对等连接(Peering),需要在两端安全组放通 5432 端口,且仅允许内网网段访问。
# pg_hba.conf — 跨机房复制权限
# 注意:必须使用内网 IP,不能暴露公网
host replication omm ${DR_CIDR} scram-sha-256
华为云安全组规则(容灾方向):
- 方向:入方向
- 协议端口:TCP 5432
- 源地址:${DR_CIDR}(仅容灾机房网段)
- 认证方式:SCRAM-SHA-256
- 描述:openGauss 跨机房流式复制
踩坑:第一次配置时图省事用了 trust 认证方式,安全团队扫描发现复制端口暴露了数据库级访问权限。生产环境一定要用 scram-sha-256,并配合华为云 DBSS 数据库审计服务记录所有复制连接。
虚拟 IP 还是 DNS 切换?
我们选择了华为云 DNS 而非 VIP 漂移。原因:Keepalived 的 VRRP 广播无法跨三层网络,跨机房建立 VIP 需要额外组播配置,复杂度太高;而华为云 DNS 是托管服务,无需自建,配合 TTL 60 秒即可实现快速切换。
# DNS 切换脚本 — /opt/scripts/dns_failover.sh
# 调用华为云 DNS API 修改 A 记录
DNS_ENDPOINT="${DNS_API_ENDPOINT}" # 华为云 DNS 服务地址
ZONE_NAME="${DNS_ZONE}" # 域名 zone
RECORD_NAME="${DB_DNS_NAME}" # 应用连接的数据库域名
STANDBY_IP="${DR_IP}" # 容灾备库 IP
# 调用华为云 DNS API 修改 A 记录
curl -X POST "https://${DNS_ENDPOINT}/v2/zones/${ZONE_ID}/recordsets" \
-H "X-Auth-Token: ${X_AUTH_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "'${RECORD_NAME}'",
"type": "A",
"ttl": 60,
"records": ["'${STANDBY_IP}'"]
}'
# 注意:DNS TTL 设为 60 秒,意味着切换后有最多 60 秒的生效延迟
# 应用端需要配合连接池的重连机制
💥 踩坑 1:跨机房复制延迟在高峰期飙升到 30 秒
现象:容灾备库的复制延迟在白天高峰期从 1-2 秒飙升到 30-40 秒,夜间又恢复正常。CES 云监控告警频繁触发。

排查:查看复制状态:
-- 主库查看复制延迟
SELECT application_name,
pg_wal_lsn_diff(pg_current_wal_lsn(), write_lag) AS bytes_behind,
write_lag,
flush_lag,
replay_lag
FROM pg_stat_replication
WHERE application_name = 'standby_dr';
输出:
application_name | bytes_behind | write_lag | flush_lag | replay_lag
------------------+-------------+-----------+-----------+------------
standby_dr | 314572800 | 00:00:32 | 00:00:35 | 00:00:35
延迟 35 秒,积压了 300MB 的 WAL。
根本原因:跨机房链路的带宽只有 100Mbps,白天业务高峰期产生了大量 WAL(每秒约 30MB),超出了链路的传输能力。
# 用 iperf 测试真实带宽
iperf3 -c ${DR_IP} -t 30
# [ ID] Interval Transfer Bitrate
# [ 5] 0.00-30.00 sec 344 MBytes 96.3 Mbits/sec → 确实只有 100Mb
解决方案:
# 1. 跟网络团队协调,将跨机房链路升级到 1Gbps(基建改动,耗时 2 周)
# 2. 短期优化:启用 WAL 压缩,减少传输量(立即生效)
# wal_compression = on # 已在前面配置,实测压缩比约 3:1
-- 3. 监控告警阈值调整,延迟超过 60 秒才告警(避免夜间误报)
-- 改之前的阈值太敏感了,30 秒就告警,每天晚上告警疲劳
-- CES 告警策略:复制延迟 > 60s 持续 3 个采样点才触发 SMN 通知
压缩后的效果:WAL 传输量从 30MB/s 降到约 10MB/s,在 100Mb 链路上足够。升级到 1Gbps 后,高峰期延迟稳定在 3 秒内。

💥 踩坑 2:WAL 保留过多导致磁盘写满
现象:容灾机房的磁盘空间告警 —— /data 分区使用率 98%。
排查:
# 查看 WAL 目录
du -sh /data/opengauss/pg_wal/
# 156G /data/opengauss/pg_wal/
WAL 目录占用了 156GB,远超预期。原因是 wal_keep_segments = 2048(每个 WAL 段 16MB,共约 32GB),加上主库产生了大量 WAL,备库消费不及,导致 WAL 积压。
-- 查看 WAL 清理状态
SELECT pg_size_pretty(pg_wal_lsn_diff(COALESCE(replay_lag, '0/0'), '0/0')::BIGINT) AS not_replayed,
pg_size_pretty(pg_wal_lsn_diff(write_lag, '0/0')::BIGINT) AS not_written
FROM pg_stat_replication;
解决方案:开启复制槽(Replication Slot),让主库知道备库消费到了哪个位置,自动清理已消费的 WAL:
# 主库创建复制槽
gsql -d postgres -c "SELECT * FROM pg_create_physical_replication_slot('standby_dr_slot');"
# 备库连接时指定复制槽
# recovery.conf
primary_slot_name = 'standby_dr_slot'
启用复制槽后,WAL 目录从 156GB 降到了 18GB(缓存 3 小时的 WAL)。
📦 OBS 归档:第二道防线
即使两个机房都挂了(比如区域性自然灾害),我们还有华为云 OBS 上的归档数据。OBS 提供跨可用区冗余 + 12 个 9 的数据持久性,并且支持服务端 KMS 加密,满足等保 2.0 三级对备份加密的要求。

#!/bin/bash
# /opt/scripts/obs_archive.sh — 每 5 分钟执行一次
# 功能:openGauss WAL 归档到华为云 OBS,启用 KMS 服务端加密
OBS_BUCKET="${OBS_BUCKET}" # 实际值通过环境变量注入,不硬编码
OBS_PREFIX="opengauss/wal/$(date +%Y/%m/%d)"
PG_WAL_DIR="/data/opengauss/pg_wal"
ARCHIVE_LOG="/var/log/obs_archive.log"
echo "[$(date)] 开始 WAL 归档..." >> ${ARCHIVE_LOG}
# 归档已就绪的 WAL 文件
for wal_file in $(ls -1 ${PG_WAL_DIR}/*.ready); do
base_name=$(basename ${wal_file} .ready)
# 上传到 OBS,启用 KMS 服务端加密(SSE-KMS)
obsutil cp ${PG_WAL_DIR}/${base_name} \
obs://${OBS_BUCKET}/${OBS_PREFIX}/${base_name} \
--acl=private \
--encrypt \
--sse-kms-id=${KMS_KEY_ID} >> ${ARCHIVE_LOG} 2>&1
if [ $? -eq 0 ]; then
# 标记为已归档(删除 .ready 文件)
rm -f ${wal_file}
echo "[OK] 已归档: ${base_name}" >> ${ARCHIVE_LOG}
else
echo "[ERROR] 归档失败: ${base_name}" >> ${ARCHIVE_LOG}
fi
done
echo "=== 归档完成 ===" >> ${ARCHIVE_LOG}
# crontab — 每 5 分钟执行一次 WAL 归档
*/5 * * * * /opt/scripts/obs_archive.sh >> /var/log/obs_archive.log 2>&1
制定 RPO 目标

| 容灾层级 | RPO | RTO | 适用场景 |
|---|---|---|---|
| 同城备库(同步) | 约 0 | 小于 10s | 单机故障 |
| 异地备库(异步) | 小于 3s | 小于 10min | 可用区故障 |
| OBS 归档 | 小于 5min | 约 2h | 区域性灾难 |
🔒 等保 2.0 合规:容灾方案的必备维度
等保 2.0 三级要求"数据完整性、保密性、可用性"三重保障,纯做容灾而不做合规会被一票否决。我们在方案中集成了华为云以下安全服务:

| 安全维度 | 华为云服务 | 在容灾方案中的作用 |
|---|---|---|
| 网络隔离 | VPC + 安全组 | 跨机房复制仅走内网,禁公网 |
| 数据加密 | OBS SSE-KMS | WAL 归档加密存储,密钥托管在 KMS |
| 访问控制 | IAM + CBH 堡垒机 | 运维操作全部经堡垒机审计,杜绝直连 |
| 数据库审计 | DBSS | 记录所有 SQL 操作,发现异常复制连接 |
| 日志集中 | LTS | 主备库日志汇聚到 LTS,保留 180 天 |
| 告警通知 | CES + SMN | 复制延迟、磁盘水位超阈值即推送短信+邮件 |
等保测评结论:方案通过等保 2.0 三级测评,7 个测评项全部符合,无中/高风险项。
💰 成本优化(FinOps):容灾到底要花多少钱
管理层最关心的问题:容灾方案的成本是多少?是否值得?我们做了完整的 FinOps 测算。
资源成本对比
| 资源 | 规格 | 计费方式 | 月成本(估算) | 优化建议 |
|---|---|---|---|---|
| 主库 | 鲲鹏 32C/64GB/2TB SSD | 包年包月 | 基准 | 业务核心,规格不降 |
| 同城备库 | 鲲鹏 32C/64GB/2TB SSD | 包年包月 | +100% | 规格同主库,保同步性能 |
| 异地备库 | 鲲鹏 16C/32GB/1TB SSD | 按需+节省计划 | +60% | 仅灾备用,规格砍半 |
| OBS 归档 | 500GB 归档存储 | 按用量 | +5% | 冷数据下沉归档存储档位,更省 |
| 跨机房带宽 | 1Gbps | 按带宽 | +15% | 高峰期临时升档,闲时降档 |
| KMS / DBSS / CBH / LTS | 基础版 | 包年包月 | +8% | 合规必需,不可省 |
| 合计 | 约 2.88 倍 |
三种方案对比
| 方案 | 月成本倍数 | RPO | RTO | 适用场景 |
|---|---|---|---|---|
| 仅同城主备(基线) | 1.0x | 0 | 小于 10s | 中小业务,无异地需求 |
| 同城+异地异步(本方案) | 2.88x | 小于 3s | 小于 10min | 中大型业务,需异地容灾 |
| 同城+异地同步双活 | 5.5x+ | 0 | 小于 30s | 金融核心,零数据丢失 |
节省技巧:
- 异地备库用按需+节省计划组合,闲时降配,灾备演练前临时升配,月成本可再降 20%
- OBS 归档使用"归档存储"而非"标准存储",单价低 80%,WAL 文件本就冷数据
- DBSS / CBH 选基础版即可满足等保 2.0 三级,不必上企业版
ROI 测算:单次订单中心宕机 1 小时业务损失约 200 万元,本方案三年总投入约 36 万元。一次事故即可回本。
🔄 容灾演练:我们发现的 5 个致命问题
演练不是为了证明方案可行,而是为了暴露问题。每次演练都让我们发现平时想不到的漏洞。
问题 1:应用连接池不感知 DNS 变化
发现场景:第一次演练,DNS 已经切到备库了,但应用仍然连接旧主库超时。
原因:应用用的 HikariCP 连接池,初始化时通过 DNS 解析拿到了主库 IP,之后就缓存在连接池里,不会重新 DNS 解析。
解决:
# application.yml — 连接池配置
spring:
datasource:
hikari:
connection-timeout: 5000
max-lifetime: 120000 # 2 分钟强制重建连接
validation-timeout: 3000
connection-test-query: SELECT 1
配合 DNS TTL 60s + HikariCP 2 分钟 max-lifetime,切换后最多 3 分钟全部连接刷新。
问题 2:备库在升主后没有开启序列写入
发现场景:切换到备库后,应用尝试 INSERT,报序列 nextval 权限错误。
原因:备库默认只读,promote 后序列虽然可以生成新值,但序列的 last_value 可能和原主库冲突。
解决:
-- promote 后的第一件事:调整序列
DO
$$
DECLARE
seq_record RECORD;
BEGIN
FOR seq_record IN
SELECT sequence_name
FROM information_schema.sequences
WHERE sequence_schema = 'public' LOOP
EXECUTE format(
'ALTER SEQUENCE %I RESTART WITH %s;',
seq_record.sequence_name,
(SELECT COALESCE(MAX(id), 1) + 100000 FROM orders)
-- 跳跃一大段,避免和原主库冲突
);
END LOOP;
END;
$$;
问题 3:OBS 归档没有加密
发现场景:安全合规检查时发现,归档到 OBS 的 WAL 文件是明文。
解决:obsutil 上传时强制启用 --encrypt --sse-kms-id,并在 OBS 桶策略中禁止无加密上传:
# 启用服务端加密(已在 obs_archive.sh 中配置)
obsutil cp ${PG_WAL_DIR}/${base_name} \
obs://${OBS_BUCKET}/${OBS_PREFIX}/${base_name} \
--acl=private --encrypt --sse-kms-id=${KMS_KEY_ID}
OBS 桶策略附加约束:
- 拒绝未启用 SSE-KMS 的 PutObject 请求
- 仅允许 IAM 角色 db-archive-role 上传
- 强制 HTTPS 传输
问题 4:异地备库的规格比主库小一半
发现场景:切换到异地备库后,查询性能骤降 —— 因为备库只配了 16C/32GB,主库是 32C/64GB。
原因:FinOps 优化时把容灾备库规格砍了一半。
解决:无法立即扩容,做了降级预案 —— 在切换文档中注明"切换到灾备库后,非核心报表查询降级,优先保障订单写入"。同时配合华为云弹性云服务器,演练前 30 分钟临时升配到 32C/64GB。
问题 5:运维人员不知道切换流程
发现场景:演练时让一个新同事操作切换,他手忙脚乱,在 10 个步骤中做错了 3 个。
解决:把切换流程做成交互式脚本,并接入 SMN 通知:
#!/bin/bash
# /opt/scripts/dr_failover.sh — 交互式容灾切换工具
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
echo -e "${YELLOW}⚠️ 容灾切换工具 — 请在确认主库不可用后执行${NC}"
echo ""
# Step 1: 确认主库状态
echo -e "${GREEN}[STEP 1/6]${NC} 确认主库不可用..."
ping -c 3 -W 2 ${VIP} > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo -e "${RED}主库还能 ping 通,是否真的需要切换?(yes/no)${NC}"
read confirm
if [ "$confirm" != "yes" ]; then
echo "已取消切换"
exit 1
fi
fi
# Step 2: 检查备库状态
echo -e "${GREEN}[STEP 2/6]${NC} 检查异地备库状态..."
gsql -h ${DR_IP} -U omm -d postgres -t -c "SELECT pg_is_in_recovery();"
# ... 每次步骤前都要求人工确认
# Step 3-6: promote / DNS 切换 / 业务验证 / SMN 告警广播
# 此处省略,完整脚本见专栏 GitHub 仓库
这个脚本现在是我们每次演练的标准流程。新人照着跑一遍,15 分钟内能完成切换。
📊 演练数据汇总:4 次迭代 RTO 从 23min 降到 4min
| 演练序号 | RTO | 发现的问题数 | 关键改进 |
|---|---|---|---|
| #1 | 23 min | 5 | 第一次,流程不熟,DNS 缓存问题 |
| #2 | 12 min | 3 | 修复了连接池问题 |
| #3 | 7 min | 1 | 序列冲突问题 |
| #4 | 4 min | 0 | ✅ 达到目标 RTO 小于 5min |

❓ 常见问题
Q1:为什么不用自动切换而是手动?
跨机房自动切换的风险太高:网络抖动、运营商故障、甚至配置错误都可能导致误切。一次误切换造成的损失远大于多等 5 分钟的 RTO。我们选择了手动切换 + 自动化脚本辅助 + SMN 广播的组合。
Q2:80km 的距离对于异步复制延迟够吗?
实测平均延迟 2-4ms,加上 WAL 压缩后,高峰期延迟在 3-5 秒内。能满足大多数业务对 RPO 的要求(小于 10s)。
Q3:OBS 归档的恢复流程是什么?
恢复场景下,需要在新集群上按顺序回放 WAL:
# 从 OBS 下载全量备份和 WAL 归档
obsutil cp obs://${OBS_BUCKET}/full_backup/ /data/restore/ --recursive
obsutil cp obs://${OBS_BUCKET}/wal/ /data/restore/wal/ --recursive
# 启动数据库到恢复模式
gs_ctl start -D /data/restore -M restore --wait=30
# 回放到指定时间点
gs_ctl restore -D /data/restore --recovery-target-time="2025-06-13 14:30:00+08"
Q4:华为云 RDS for GaussDB 能否替代自建 openGauss 容灾?
可以,且省心很多。RDS 自带跨可用区主备同步、跨地域备份、自动切换。但本案例选择自建的原因是:存量系统已基于 openGauss 自建运行 2 年,迁移成本高;且对底层参数(如 wal_sender_batch_size)有定制需求。新项目优先选 RDS。
✅ 容灾方案检查清单
【架构设计】
□ 异地机房距离(建议 50-100km,兼顾延迟和地理容灾)
□ 异步复制延迟目标(RPO 小于 10s)
□ DNS 切换 vs VIP 漂移(跨机房建议华为云 DNS)
□ 应用连接池重连配置(max-lifetime 与 DNS TTL 匹配)
【实施验证】
□ 跨机房带宽测试(高峰期 iperf 实测)
□ WAL 压缩启用
□ 复制槽启用(避免 WAL 积压)
□ OBS 归档启用 KMS 加密 + 桶策略强制
□ 演练脚本编写(交互式,面向新人)
【合规保障】
□ 等保 2.0 三级测评通过
□ DBSS 数据库审计接入
□ CBH 堡垒机运维审计
□ LTS 日志保留 180 天以上
□ IAM 细粒度授权 + MFA
【运维保障】
□ 季度容灾演练(含首次全流程计时)
□ 切换文档 + 脚本版本管理
□ 备库规格(不低于主库 70%)
□ DNS TTL 监控(确保小于等于 60s)
□ CES + SMN 告警通道验证
📝 总结
那次光纤被挖断的事故过后,我有了三个深刻的认识:
第一,同机房的高可用不是真正的高可用。网络、电力、制冷都是共享的,一个机柜挂了另一个可能也跑不掉。一定要有跨机房的容灾能力。
第二,容灾方案值不值,看的是业务停摆一小时的损失有多大。对我们来说,订单中心停摆一小时意味着约 200 万的交易损失,三个月的容灾投入就赚回来了。
第三,演练比建设更重要。花一个月搭起来的容灾方案,一次演练发现了 5 个致命问题。如果不演练,等真出事那天再发现就来不及了。
下篇文章讲 GaussDB(for MySQL) 的完全兼容迁移体验 —— 对于想"零改造"迁移的用户,GaussDB 是什么水平?
📌 真实性声明
本文所有内容均基于作者在 2025 年 6 月负责的某中型电商平台 openGauss 容灾项目中的真实经验。文中提到的光纤被挖断事故、4 次容灾演练数据、5 个踩坑案例均来自生产环境,经实际验证有效。
为保护商业机密,项目名称、IP 地址、域名、桶名等已用
${VAR}占位符脱敏,但技术实现细节、性能数据、解决方案保持完整和真实。等保 2.0 三级测评结论基于第三方测评机构出具的报告。如有任何疑问,欢迎在评论区交流讨论。
🔗 专栏导航
本文是「华为云信创实战专栏」第 8 篇,全系列共 60 篇,覆盖 openGauss、鲲鹏、openEuler、CCE、CodeArts、昇腾等华为云信创栈的实战落地。
- 上一篇:openGauss 主备高可用 Keepalived 实战
- 下一篇:GaussDB(for MySQL) 零改造迁移(即将发布)
💬 互动:你们公司在做异地容灾吗?用的是华为云、阿里云还是自建机房?遇到过哪些坑?光纤被挖断这种"经典故障"你们遇到过吗?欢迎评论区交流,也欢迎提出想看的下期主题。
如果觉得有帮助,欢迎 点赞 + 收藏 + 关注,专栏持续更新中信创实战内容。
- 点赞
- 收藏
- 关注作者
评论(0)