欢迎光临
我们一直在努力

深入了解MongoDB 两地三中心架构

一、概述

在企业级数据库架构中,“两地三中心” 是一种经典的容灾部署模式——即在同一城市的两个数据中心(同城双活)加一个异地灾备中心,构成三层防护体系。

对于 MongoDB 而言,这是通过 副本集(Replica Set) 来实现的。


二、MongoDB 的容灾体系

2.1 容灾级别演进

MongoDB 提供了多层次的容灾能力,从低到高依次为:

#mermaid-svg-XyProj8NVFQ3H5FP{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XyProj8NVFQ3H5FP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XyProj8NVFQ3H5FP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XyProj8NVFQ3H5FP .error-icon{fill:#552222;}#mermaid-svg-XyProj8NVFQ3H5FP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XyProj8NVFQ3H5FP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XyProj8NVFQ3H5FP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XyProj8NVFQ3H5FP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XyProj8NVFQ3H5FP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XyProj8NVFQ3H5FP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XyProj8NVFQ3H5FP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XyProj8NVFQ3H5FP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XyProj8NVFQ3H5FP .marker.cross{stroke:#333333;}#mermaid-svg-XyProj8NVFQ3H5FP svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XyProj8NVFQ3H5FP p{margin:0;}#mermaid-svg-XyProj8NVFQ3H5FP .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-XyProj8NVFQ3H5FP .cluster-label text{fill:#333;}#mermaid-svg-XyProj8NVFQ3H5FP .cluster-label span{color:#333;}#mermaid-svg-XyProj8NVFQ3H5FP .cluster-label span p{background-color:transparent;}#mermaid-svg-XyProj8NVFQ3H5FP .label text,#mermaid-svg-XyProj8NVFQ3H5FP span{fill:#333;color:#333;}#mermaid-svg-XyProj8NVFQ3H5FP .node rect,#mermaid-svg-XyProj8NVFQ3H5FP .node circle,#mermaid-svg-XyProj8NVFQ3H5FP .node ellipse,#mermaid-svg-XyProj8NVFQ3H5FP .node polygon,#mermaid-svg-XyProj8NVFQ3H5FP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XyProj8NVFQ3H5FP .rough-node .label text,#mermaid-svg-XyProj8NVFQ3H5FP .node .label text,#mermaid-svg-XyProj8NVFQ3H5FP .image-shape .label,#mermaid-svg-XyProj8NVFQ3H5FP .icon-shape .label{text-anchor:middle;}#mermaid-svg-XyProj8NVFQ3H5FP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XyProj8NVFQ3H5FP .rough-node .label,#mermaid-svg-XyProj8NVFQ3H5FP .node .label,#mermaid-svg-XyProj8NVFQ3H5FP .image-shape .label,#mermaid-svg-XyProj8NVFQ3H5FP .icon-shape .label{text-align:center;}#mermaid-svg-XyProj8NVFQ3H5FP .node.clickable{cursor:pointer;}#mermaid-svg-XyProj8NVFQ3H5FP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XyProj8NVFQ3H5FP .arrowheadPath{fill:#333333;}#mermaid-svg-XyProj8NVFQ3H5FP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XyProj8NVFQ3H5FP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XyProj8NVFQ3H5FP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XyProj8NVFQ3H5FP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XyProj8NVFQ3H5FP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XyProj8NVFQ3H5FP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XyProj8NVFQ3H5FP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XyProj8NVFQ3H5FP .cluster text{fill:#333;}#mermaid-svg-XyProj8NVFQ3H5FP .cluster span{color:#333;}#mermaid-svg-XyProj8NVFQ3H5FP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XyProj8NVFQ3H5FP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XyProj8NVFQ3H5FP rect.text{fill:none;stroke-width:0;}#mermaid-svg-XyProj8NVFQ3H5FP .icon-shape,#mermaid-svg-XyProj8NVFQ3H5FP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XyProj8NVFQ3H5FP .icon-shape p,#mermaid-svg-XyProj8NVFQ3H5FP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XyProj8NVFQ3H5FP .icon-shape .label rect,#mermaid-svg-XyProj8NVFQ3H5FP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XyProj8NVFQ3H5FP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XyProj8NVFQ3H5FP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XyProj8NVFQ3H5FP :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

单节点无容灾

副本集同机房 HA

副本集同城双中心

副本集两地三中心

分片集群多地域部署

级别架构容灾能力适用场景
L1 单节点 无 开发测试
L2 副本集(同机房) 单机故障自动切换 一般生产环境
L3 副本集(同城双中心) 单机房故障存活 核心业务
L4 副本集(两地三中心) 城市级灾难存活 金融/政务
L5 分片集群 + 多地域 跨地域容灾 全球化业务

2.2 什么是两地三中心

┌──────────────────────────────────────────────────────┐
│ 城市A(主站点) │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 数据中心 1 │ │ 数据中心 2 │ │
│ │ (主数据中心) │ │ (同城灾备中心) │ │
│ │ │ │ │ │
│ │ ┌───┐ ┌───┐ │ │ ┌───┐ ┌───┐ │ │
│ │ │P0 │ │P1 │ │ │ │S0 │ │S1 │ │ │
│ │ └───┘ └───┘ │ │ └───┘ └───┘ │ │
│ │ 高优先级节点 │ │ 普通优先级节点 │ │
│ └─────────────────┘ └─────────────────┘ │
│ │ │ │
│ │ 低延迟专用网络 │ │
│ └───────────────────────┘ │
└──────────────────────────────────────────────────────┘
│
│ 跨城网络
│
┌──────────────────────────────────────────────────────┐
│ 城市B(异地灾备) │
│ ┌─────────────────┐ │
│ │ 数据中心 3 │ │
│ │ (异地灾备中心) │ │
│ │ │ │
│ │ ┌───┐ │ │
│ │ │A0 │ │ ← 仅投票 / 延迟数据同步 │
│ │ └───┘ │ │
│ └─────────────────┘ │
└──────────────────────────────────────────────────────┘


三、两地三中心设计要点

3.1 节点数量

推荐采用 2 + 2 + 1 = 5 节点 的分布模式:

位置节点数角色说明
主数据中心(DC1) 2 PRIMARY 候选 高优先级,承载读写
同城灾备中心(DC2) 2 SECONDARY 普通优先级,同城同步
异地灾备中心(DC3) 1 SECONDARY(可设延迟) 异地容灾,仅投票

为什么是 5 个节点? MongoDB 副本集选举需要多数派(majority),5 节点可容忍 2 个节点故障。如果只有 3 个节点,任意一个数据中心(2 节点)宕机就会失去多数派。

3.2 选举优先级

// 将主数据中心的节点优先级调高
// 主数据中心 DC1 节点
{ priority: 10 } // member1 — 最优先当选 PRIMARY
{ priority: 5 } // member2

// 同城灾备 DC2 节点
{ priority: 1 } // member3
{ priority: 1 } // member4

// 异地灾备 DC3 节点
{ priority: 0.5 } // member5 — 仅在紧急情况下当选

设置更高优先级的目的是 避免跨城切换 PRIMARY,因为异地网络延迟高,跨城选举 PRIMARY 会严重影响写入性能。

3.3 网络要求

路径要求原因
DC1 ↔ DC2(同城) 低延迟(< 5ms),高带宽 writeConcern: majority 需要同城双写
DC1/DC2 → DC3(跨城) 容忍较高延迟 异地节点以异步复制为主

3.4 Write Concern 配置

// 推荐配置:数据写入需要多数节点确认
db.collection.insertOne(
{ data: "critical" },
{ writeConcern: { w: "majority", wtimeout: 5000 } }
)

在 2+2+1 架构中,majority = 3。这意味着每次写入必须至少同步到 3 个节点(DC1 + DC2 各至少一个)才算成功。


四、环境准备

4.1 拓扑规划

虚拟机IP部署节点所属数据中心
geekdemo1 192.168.1.1 member1 (10001), member2 (10002) DC1(主数据中心)
geekdemo2 192.168.1.2 member3 (10003), member4 (10004) DC2(同城灾备)
geekdemo3 192.168.1.3 member5 (10005) DC3(异地灾备)

4.2 环境要求

  • MongoDB 4.2+
  • 3 台 Linux 虚拟机(CentOS 7 / Ubuntu 18.04+)
  • 各节点间网络互通

五、完整部署步骤

步骤 1:配置域名解析

在每台虚拟机上配置 /etc/hosts,使节点可通过域名互相访问:

# === 在 geekdemo1 (192.168.1.1) 上执行 ===
cat >> /etc/hosts << EOF
192.168.1.1 geekdemo1 member1.example.com member2.example.com
192.168.1.2 geekdemo2 member3.example.com member4.example.com
192.168.1.3 geekdemo3 member5.example.com
EOF

# === 在 geekdemo2 (192.168.1.2) 上执行 ===
cat >> /etc/hosts << EOF
192.168.1.1 geekdemo1 member1.example.com member2.example.com
192.168.1.2 geekdemo2 member3.example.com member4.example.com
192.168.1.3 geekdemo3 member5.example.com
EOF

# === 在 geekdemo3 (192.168.1.3) 上执行 ===
cat >> /etc/hosts << EOF
192.168.1.1 geekdemo1 member1.example.com member2.example.com
192.168.1.2 geekdemo2 member3.example.com member4.example.com
192.168.1.3 geekdemo3 member5.example.com
EOF

验证解析:

# 在三台机器上互相 ping 测试
ping -c 2 member1.example.com
ping -c 2 member3.example.com
ping -c 2 member5.example.com

步骤 2:创建数据目录并启动 MongoDB 实例

# ===== geekdemo1(DC1:启动 member1 和 member2)=====
mkdir -p ~/data/member1 ~/data/member2

mongod –dbpath ~/data/member1 \\
–replSet demo \\
–bind_ip 0.0.0.0 \\
–port 10001 \\
–fork \\
–logpath ~/data/member1.log \\
–oplogSize 1024

mongod –dbpath ~/data/member2 \\
–replSet demo \\
–bind_ip 0.0.0.0 \\
–port 10002 \\
–fork \\
–logpath ~/data/member2.log \\
–oplogSize 1024

# ===== geekdemo2(DC2:启动 member3 和 member4)=====
mkdir -p ~/data/member3 ~/data/member4

mongod –dbpath ~/data/member3 \\
–replSet demo \\
–bind_ip 0.0.0.0 \\
–port 10003 \\
–fork \\
–logpath ~/data/member3.log \\
–oplogSize 1024

mongod –dbpath ~/data/member4 \\
–replSet demo \\
–bind_ip 0.0.0.0 \\
–port 10004 \\
–fork \\
–logpath ~/data/member4.log \\
–oplogSize 1024

# ===== geekdemo3(DC3:启动 member5)=====
mkdir -p ~/data/member5

mongod –dbpath ~/data/member5 \\
–replSet demo \\
–bind_ip 0.0.0.0 \\
–port 10005 \\
–fork \\
–logpath ~/data/member5.log \\
–oplogSize 1024

参数说明:

参数含义
–replSet demo 副本集名称
–bind_ip 0.0.0.0 绑定所有网卡
–fork 后台守护进程模式
–oplogSize 1024 oplog 大小(MB),生产环境建议更大

验证所有实例均已启动:

# 在任意节点上测试连通性
mongo –eval "db.runCommand({ping:1})" member1.example.com:10001
mongo –eval "db.runCommand({ping:1})" member2.example.com:10002
mongo –eval "db.runCommand({ping:1})" member3.example.com:10003
mongo –eval "db.runCommand({ping:1})" member4.example.com:10004
mongo –eval "db.runCommand({ping:1})" member5.example.com:10005

步骤 3:初始化副本集

连接到 member1 并执行初始化:

// 连接到 member1
mongo member1.example.com:10001

// 初始化副本集
rs.initiate({
_id: "demo",
version: 1,
members: [
{ _id: 0, host: "member1.example.com:10001" },
{ _id: 1, host: "member2.example.com:10002" },
{ _id: 2, host: "member3.example.com:10003" },
{ _id: 3, host: "member4.example.com:10004" },
{ _id: 4, host: "member5.example.com:10005" }
]
})

查看副本集状态:

rs.status()
// 应看到 member1 被选举为 PRIMARY(_id:0 默认优先)

步骤 4:配置选举优先级

// 获取当前配置
var cfg = rs.conf()

// member1(DC1)— 最高优先级
cfg.members[0].priority = 10

// member2(DC1)— 次高优先级
cfg.members[1].priority = 5

// member3、member4(DC2)— 保持默认 1
// cfg.members[2].priority = 1
// cfg.members[3].priority = 1

// member5(DC3)— 更低优先级,仅兜底
cfg.members[4].priority = 0.5

// 应用配置
rs.reconfig(cfg)

验证优先级:

rs.conf().members.forEach(function(m) {
print("Host: " + m.host + ", Priority: " + m.priority)
})

步骤 5:配置异地节点延迟同步(可选)

如果希望异地节点刻意延迟同步,防止误操作或逻辑错误被即时同步到灾备中心:

var cfg = rs.conf()
// member5 延迟 1 小时同步
cfg.members[4].secondaryDelaySecs = 3600
rs.reconfig(cfg)

步骤 6:编写持续写入脚本

创建 ingest.js 用于模拟持续写入:

// ingest.js — 每2秒插入一条记录,验证写入成功
db = db.getSiblingDB("test")
db.test.drop()

for (var i = 1; i <= 1000; i++) {
var result = db.test.insertOne({
item: i,
ts: new Date().getTime() / 1000
})

var inserted = db.test.findOne({ item: i })
if (inserted) {
print("✓ Item " + i + " inserted at " + new Date().toISOString())
} else {
print("✗ Unexpected failure at item " + i)
}
sleep(2000)
}

启动持续写入(使用副本集连接串以支持自动故障转移):

mongo –retryWrites \\
"mongodb://member1.example.com:10001,member2.example.com:10002,member3.example.com:10003,member4.example.com:10004,member5.example.com:10005/test?replicaSet=demo" \\
ingest.js

–retryWrites 确保在 PRIMARY 切换时,写入操作能自动重试。MongoDB 4.2+ 驱动默认启用,但命令行中显式声明更安全。


六、容灾演练

6.1 模拟从数据中心故障(DC2 宕机)

操作:停止 geekdemo2 上的所有 MongoDB 进程

# 在 geekdemo2 上执行
pkill mongod

预期结果:

  • 副本集仍有 3 个节点存活(member1, member2, member5),满足 majority(3/5)
  • PRIMARY 不发生切换(member1 和 member2 仍在 DC1)
  • 持续写入脚本不受影响

验证:

rs.status().members.forEach(function(m) {
print(m.name + " → " + m.stateStr)
})
// member3 和 member4 应显示为 (not reachable/health check failed)

6.2 模拟主数据中心故障(DC1 宕机)

操作:停止 geekdemo1 上的所有 MongoDB 进程

# 在 geekdemo1 上执行
pkill mongod

预期结果:

  • DC2 的 member3 或 member4 自动被选举为新 PRIMARY
  • 写入脚本自动重试连接到新 PRIMARY
  • 数据不丢失(已写入 majority 的数据得到保留)

验证:

rs.status().members.forEach(function(m) {
print(m.name + " → " + m.stateStr)
})
// member3 或 member4 应成为 PRIMARY

6.3 故障恢复

# 在 geekdemo1 和 geekdemo2 上重新启动 mongod 进程
# 恢复后,由于 DC1 节点优先级更高,PRIMARY 会自动切回 DC1

6.4 极限测试:双中心同时故障

当 DC1 和 DC2 同时宕机,仅剩 DC3 的 member5:

  • 存活节点数 1 < majority(3),副本集进入 只读状态
  • 无法自动恢复,需要手动干预:

// 在 member5 上强制重新配置(紧急恢复用)
rs.reconfig({
_id: "demo",
version: 2,
members: [
{ _id: 4, host: "member5.example.com:10005" }
]
}, { force: true })

强制重配置是紧急手段,可能导致数据回滚,谨慎使用。


七、监控要点

7.1 关键监控指标

// 副本集状态概览
rs.status()

// 复制延迟
rs.printSecondaryReplicationStatus()

// Oplog 窗口(防止 oplog 被覆盖导致全量同步)
db.getReplicationInfo()

7.2 告警规则建议

指标阈值说明
复制延迟 > 10s 可能网络异常或从节点负载过高
Oplog 窗口 < 1h 存在全量同步风险
节点存活数 < 3(5节点集) 即将丢失 majority
PRIMARY 位置 不在 DC1 可能是主数据中心故障

八、备注

  • Oplog 大小:生产环境建议 ≥ 磁盘容量的 5%,确保有充足的回放窗口
  • 认证与授权:务必开启 –auth 并使用 keyfile 进行副本集内部认证
  • 网络隔离:异地中心之间建议使用 VPN 或专线
  • 备份策略:异地灾备中心不应替代备份——仍需独立的备份方案(mongodump / 文件系统快照)
  • 定期演练:至少每季度执行一次完整的容灾切换演练

  • 九、总结

    要点说明
    架构模式 2(DC1)+ 2(DC2)+ 1(DC3)= 5 节点
    优先级分布 DC1 高优先级 → 避免不必要的跨城切换
    多数派原理 5 节点 majority = 3,可容忍 2 节点故障
    网络依赖 同城需低延迟,跨城可容忍高延迟
    异地节点 可设置 secondaryDelaySecs 防止误删同步
    极限场景 双中心同时宕机需手动 force 恢复

    MongoDB 副本集的"两地三中心"架构是经过大量生产环境验证的成熟方案,合理配置优先级和 Write Concern,可以在数据安全与写入性能之间取得最佳平衡。

    赞(0)
    未经允许不得转载:171主机测评 » 深入了解MongoDB 两地三中心架构
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址