MongoDB 复制(Replica Set)全面指南
MongoDB 的 副本集(Replica Set) 是实现 高可用(HA)、数据冗余 和 读写分离 的核心机制。一个副本集由多个节点组成,相互复制数据,其中 一个主节点(Primary) 接受写操作,其余 从节点(Secondary) 实时同步数据。
一、核心概念
| 术语 | 说明 |
|---|
| Primary(主节点) | 唯一接受写操作的节点,选举产生 |
| Secondary(从节点) | 从 Primary 同步数据,可提供读操作 |
| Arbiter(仲裁节点) | 只参与选举,不存数据,仅用于避免脑裂 |
| Oplog(Operation Log) | 主节点记录所有写操作的日志,从节点通过 oplog 拉取变更 |
| Replication Lag | 从节点落后主节点的延迟时间 |
| Election(选举) | 当 Primary 宕机时,自动选举新的 Primary |
二、副本集架构示例
[Primary] ←── oplog ──→ [Secondary 1]
↑ ↓
└── oplog ──→ [Secondary 2] (可选)
└── vote ──→ [Arbiter] (仅参与选举)
三、搭建副本集(3 节点示例)
1. 配置文件(mongod.conf)
# node1.conf (端口 27017)
net:
port: 27017
replication:
replSetName: rs0
storage:
dbPath: /data/rs0-1
# node2.conf (端口 27018)
net:
port: 27018
replication:
replSetName: rs0
storage:
dbPath: /data/rs0-2
# node3.conf (端口 27019)
net:
port: 27019
replication:
replSetName: rs0
storage:
dbPath: /data/rs0-3
2. 启动节点
mongod --config node1.conf
mongod --config node2.conf
mongod --config node3.conf
3. 初始化副本集(在任意节点执行)
// 连接到 node1
mongosh --port 27017
// 初始化
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "localhost:27017" },
{ _id: 1, host: "localhost:27018" },
{ _id: 2, host: "localhost:27019" }
]
})
4. 验证状态
rs.status() // 查看整体状态
rs.isMaster() // 当前节点角色
rs.conf() // 配置信息
四、节点状态一览
| 状态 | 说明 |
|---|
PRIMARY | 可读写 |
SECONDARY | 可读,同步中 |
STARTUP | 初始同步 |
STARTUP2 | 正在应用 oplog |
RECOVERING | 恢复中(不可读) |
ARBITER | 仲裁节点 |
DOWN / UNKNOWN | 失联 |
rs.status().members.forEach(m => print(`${m.name}: ${m.stateStr}`))
五、读写操作与读偏好(Read Preference)
| 读偏好 | 说明 | 适用场景 |
|---|
primary | 默认,只读主节点 | 强一致性 |
primaryPreferred | 优先主,失败则读从 | 高可用读 |
secondary | 只读从节点 | 减轻主节点压力 |
secondaryPreferred | 优先从,主不可用时读主 | 推荐用于查询 |
nearest | 读延迟最低的节点 | 跨地域部署 |
连接字符串示例
// 读从节点(推荐用于报表)
mongodb://host1:27017,host2:27018,host3:27019/mydb?replicaSet=rs0&readPreference=secondaryPreferred
代码中使用
// Node.js (mongodb driver)
const client = new MongoClient(uri, {
readPreference: ReadPreference.SECONDARY_PREFERRED
})
六、写关注(Write Concern)
控制写操作确认的副本数量。
| 选项 | 说明 |
|---|
{ w: 1 } | 默认,只确认主节点 |
{ w: 2 } | 主 + 1 个从节点确认 |
{ w: "majority" } | 大多数节点确认 |
{ wtimeout: 5000 } | 超时时间(ms) |
db.collection.insertOne(
{ item: "box" },
{ writeConcern: { w: "majority", wtimeout: 5000 } }
)
七、Oplog 详解
- 存储在
local 数据库的 capped collection: oplog.rs
- 每个写操作记录为一条文档
- 从节点通过 tailable cursor 拉取 oplog
// 查看 oplog 大小
db.getSiblingDB("local").oplog.rs.stats()
// 查看最新操作
db.getSiblingDB("local").oplog.rs.find().sort({ $natural: -1 }).limit(5)
注意:oplog 太小会导致从节点无法追上主节点 → 需重新全量同步!
八、常见运维操作
1. 添加节点
rs.add("newhost:27017")
2. 删除节点
rs.remove("oldhost:27018")
3. 重新配置(调整优先级、投票)
cfg = rs.conf()
cfg.members[0].priority = 2 // 提升为首选主节点
cfg.members[1].votes = 0 // 取消投票权(转为纯从)
rs.reconfig(cfg)
4. 强制重新选举(stepDown)
rs.stepDown(60) // 主节点退位 60 秒
5. 冻结节点(暂停选举)
rs.freeze(300) // 300 秒内不参与选举
九、故障演练与恢复
| 场景 | 操作 | 预期 |
|---|
| 主节点宕机 | 关闭 Primary | 自动选举新主 |
| 从节点延迟 | 暂停同步 | rs.printSecondaryReplicationInfo() 显示 lag |
| 网络分区 | 断开网络 | 可能触发选举或降级 |
// 查看同步延迟
rs.printSecondaryReplicationInfo()
十、高级配置建议
1. 优先级(Priority):控制谁更容易当选主节点
{ _id: 0, host: "host1:27017", priority: 2 } // 高优先级
{ _id: 1, host: "host2:27018", priority: 1 }
{ _id: 2, host: "host3:27019", priority: 0.5 }
2. 隐藏节点(Hidden):用于备份、分析
{
_id: 3,
host: "backup:27020",
priority: 0,
hidden: true,
votes: 0
}
客户端需显式设置 readPreference 才能访问。
3. 延迟节点(Delayed):灾备用
{
_id: 4,
host: "dr:27021",
priority: 0,
slaveDelay: 3600, // 延迟 1 小时
hidden: true
}
十一、监控关键指标
| 指标 | 说明 | 工具 |
|---|
repl lag | 同步延迟 | rs.status() |
oplog window | oplog 可支持的追赶时间 | rs.printReplicationInfo() |
election time | 选举耗时 | 日志 |
connections | 连接数 | db.serverStatus().connections |
asserts | 错误计数 | db.serverStatus().asserts |
// 一键查看 oplog 窗口
rs.printReplicationInfo()
十二、与分片(Sharding)关系
- 副本集是分片的基础
- 每个分片(shard)是一个副本集
- 配置服务器(config server)也是一个副本集
- 路由进程(mongos)不存储数据
mongos > shard1 (replset) > shard2 (replset)
十三、常见问题排查
| 问题 | 检查点 |
|---|
从节点 RECOVERING | oplog 太小 / 网络中断 / 磁盘满 |
| 无法选举 | 多数节点不可达 / 票数不足 |
| 写操作慢 | w: majority + 高延迟 |
| 数据不一致 | 检查 readConcern / 回滚 |
十四、实战脚本:一键部署 3 节点副本集(Docker)
# docker-compose.yml
version: '3.8'
services:
mongo1:
image: mongo:7
command: --replSet rs0 --bind_ip_all
ports: [27017:27017]
volumes: [./data1:/data/db]
mongo2:
image: mongo:7
command: --replSet rs0 --bind_ip_all
ports: [27018:27017]
volumes: [./data2:/data/db]
mongo3:
image: mongo:7
command: --replSet rs0 --bind_ip_all
ports: [27019:27017]
volumes: [./data3:/data/db]
docker-compose up -d
# 初始化
mongosh --port 27017 --eval 'rs.initiate({_id:"rs0", members:[{ _id:0, host:"host.docker.internal:27017"}, { _id:1, host:"host.docker.internal:27018"}, { _id:2, host:"host.docker.internal:27019"}]})'
十五、学习资源
你想实现什么?
- 搭建测试环境?
- 配置读写分离?
- 灾备延迟节点?
- 跨机房副本集?
欢迎贴出你的 架构图 / 业务需求,我可以给出 完整配置 + 自动化脚本!