跳到主要内容

副本集

副本集(Replica Set)是 MongoDB 实现高可用的核心机制,提供数据冗余和故障自动恢复。

副本集基础

什么是副本集?

副本集是一组 MongoDB 服务器,包含一个主节点和多个从节点(副本):

副本集的优势

  1. 高可用性:主节点故障时自动选举新主节点
  2. 数据冗余:数据在多个节点保存
  3. 读写分离:可以将读操作分发到从节点
  4. 故障恢复:自动故障转移,无需人工干预

副本集架构

成员类型

成员类型作用说明
Primary主节点接收所有写操作
Secondary从节点复制主节点数据,可选接受读操作
Arbiter仲裁节点不存储数据,只参与选举

Oplog

Oplog(操作日志)是副本同步的核心:

// 主节点记录所有写操作到 oplog
// 从节点通过读取和应用 oplog 来同步数据
db.oplog.rs.find().sort({ ts: -1 })

选举机制

当主节点不可用时,副本集会进行选举:

  1. 触发条件:主节点心跳超时(默认 10 秒)
  2. 选举过程:从节点发起选举,获取多数票
  3. 选举原则:获得多数投票的节点成为新主节点

配置副本集

启动副本集成员

// 配置文件方式 (mongod.conf)
storage:
dbPath: /data/db
systemLog:
destination: file
path: /var/log/mongodb/mongod.log
replication:
replSetName: "rs0"

// 命令行方式
mongod --replSet rs0 --port 27017 --dbpath /data/db

初始化副本集

// 连接任意成员,初始化副本集
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "localhost:27017" },
{ _id: 1, host: "localhost:27018" },
{ _id: 2, host: "localhost:27019", arbiterOnly: true }
]
})

查看副本集状态

// 查看状态
rs.status()

// 查看配置
rs.conf()

// 查看成员详情
rs.isMaster()

读写策略

写入策略

// 默认写入主节点
db.users.insertOne({ name: "test" })

// 确认写入成功(默认 w: 1)
db.users.insertOne({ name: "test" }, { writeConcern: { w: "majority" } })

// 确认写入多数节点
db.users.insertOne(
{ name: "test" },
{ writeConcern: { w: "majority", wtimeout: 5000 } }
)

读取策略

// 默认从主节点读取
db.users.find()

// 从主节点读取(显式)
db.users.find().readPref("primary")

// 从最近从节点读取
db.users.find().readPref("nearest")

// 从从节点读取(可能读取到过期数据)
db.users.find().readPref("secondaryPreferred")

// 从特定标签的节点读取
db.users.find().readPref("secondary", [{ datacenter: "dc1" }])

故障转移

自动故障转移

// 模拟主节点故障
// 副本集自动选举新主节点

// 查看当前主节点
db.isMaster()

// 故障转移后的新主节点
// 之前的从节点成为新的主节点

手动故障转移

// 手动触发主节点降级(用于维护)
rs.stepDown()

// 强制重新选举
rs.stepDown(60) // 60 秒内不参与选举

副本集成员操作

// 添加成员
rs.add("localhost:27020")

// 添加仲裁节点
rs.add({ host: "localhost:27021", arbiterOnly: true })

// 移除成员
rs.remove("localhost:27020")

// 修改成员配置
var config = rs.conf()
config.members[0].priority = 2 // 提高优先级
rs.reconfig(config)

副本集连接

连接字符串

// 副本集连接
mongodb://localhost:27017,localhost:27018,localhost:27019/?replicaSet=rs0

// 带有读写策略
mongodb://localhost:27017,localhost:27018,localhost:27019/?replicaSet=rs0&readPreference=secondaryPreferred

Java 连接示例

const { MongoClient } = require('mongodb');

const uri = 'mongodb://localhost:27017,localhost:27018,localhost:27019/?replicaSet=rs0';

const client = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
readPreference: 'secondaryPreferred'
});

await client.connect();

副本集监控

// 查看复制延迟
db.adminCommand({ replSetGetStatus: 1 })
// 查看 "syncingTo" 和 "lag" 字段

// 查看 oplog 大小
db.oplog.rs.stats().maxSize

// 查看同步状态
db.printReplicationInfo()

// 监控复制延迟(定期执行)
function getReplicationLag() {
const status = rs.status();
return status.members.map(m => ({
name: m.name,
state: m.stateStr,
optime: m.optime,
lag: m.optimeDate - new Date(status.members[0].optimeDate)
}));
}

副本集最佳实践

  1. 至少 3 个成员:确保多数票选举
  2. 跨机架/机房部署:提高容灾能力
  3. 合理配置优先级:控制主节点位置
  4. 监控复制延迟:及时发现同步问题
  5. 定期检查 oplog 大小:确保不会用尽
// 推荐配置:3 个数据节点
{
_id: "rs0",
members: [
{ _id: 0, host: "node1:27017", priority: 2 },
{ _id: 1, host: "node2:27017", priority: 1 },
{ _id: 2, host: "node3:27017", priority: 1 }
]
}

// 如果需要仲裁,使用 5 个成员
{
_id: "rs0",
members: [
{ _id: 0, host: "node1:27017" },
{ _id: 1, host: "node2:27017" },
{ _id: 2, host: "node3:27017" },
{ _id: 3, host: "node4:27017", priority: 0 },
{ _id: 4, host: "node5:27017", arbiterOnly: true }
]
}

小结

本章我们学习了:

  1. 副本集基础:高可用架构和核心概念
  2. 副本集成员:Primary、Secondary、Arbiter
  3. Oplog:操作日志和同步机制
  4. 选举机制:故障转移原理
  5. 配置和管理:初始化、监控、故障转移
  6. 读写策略:读偏好和写确认
  7. 最佳实践:生产环境配置建议