ToolkitX
知识库工具箱

副本集

主从复制、自动故障转移、读写分离

30min·高级

01. 副本集是什么

副本集(Replica Set)是 MongoDB 实现高可用的核心机制——多个 mongod 进程组成一个集群,一个主节点处理所有写入,多个从节点实时同步数据。 主节点挂了怎么办?——副本集自动选举产生新的主节点,整个过程中应用可能短暂不可用(几秒到十几秒),但数据不丢、服务自动恢复。这就是高可用的含义:单点故障不导致服务中断。 副本集最少需要 3 个节点(1 主 2 从)或 2 个节点加 1 个仲裁者(只投票不存数据)。为什么是奇数?因为选举需要多数派(超过半数)——偶数节点可能分裂成平票两个阵营谁也选不出来。 应用端连副本集:连接字符串里写所有节点地址,驱动自动发现主节点。主节点变了驱动自动切换连接。
bash
# 启动副本集(每个节点都要指定相同的 replSet)
mongod --replSet myReplSet --port 27017 --dbpath /data/rs1
mongod --replSet myReplSet --port 27018 --dbpath /data/rs2
mongod --replSet myReplSet --port 27019 --dbpath /data/rs3

# 在任一节点上初始化副本集
mongosh --port 27017
rs.initiate({
  _id: "myReplSet",
  members: [
    { _id: 0, host: "localhost:27017" },
    { _id: 1, host: "localhost:27018" },
    { _id: 2, host: "localhost:27019" }
  ]
});
副本集成员数最好是奇数。如果只有 2 个数据节点,加一个仲裁者(arbiter)——它不存数据只投票,资源消耗极小。

02. 复制流程——Oplog 机制

MongoDB 的复制基于 Oplog(操作日志)。主节点把所有的写操作(增删改)记录到 oplog 里,从节点持续拉取 oplog 并在本地重放。 Oplog 是一个固定大小的集合(capped collection),存在 local 数据库的 oplog.rs 里。满了就覆盖最老的记录。如果从节点落后太多,oplog 已经滚过了,从节点就追不上需要全量同步。 Oplog 大小默认是空闲磁盘的 5%(最小 990MB,最大 50GB)。高写入场景要调大 oplog,避免从节点追不上。 从节点拉取 oplog 是异步的,所以主从之间会有延迟(Replication Lag)。延迟大小取决于写入量、网络速度、从节点性能。
javascript
// 查看 oplog 信息
use local;
db.oplog.rs.stats();

// 查看 oplog 最新一条
db.oplog.rs.find().sort({ $natural: -1 }).limit(1);

// 查看复制状态
rs.printReplicationInfo();   // 主节点:oplog 大小和时间
rs.printSecondaryReplicationInfo();   // 各从节点的复制延迟
rs.printReplicationInfo() 能看到 oplog 能保存多长时间的操作记录。如果时间太短,从节点容易追不上。

03. 读写分离与 Read Preference

默认情况下从节点不处理读请求——所有读写都走主节点。但可以配置 Read Preference 让读请求分散到从节点,减轻主节点压力。 Read Preference 模式: primary(默认)——所有读走主节点 primaryPreferred——优先读主,主挂了读从 secondary——只读从节点 secondaryPreferred——优先读从,从都挂了读主 nearest——读网络延迟最低的节点 读写分离的代价:可能读到旧数据。因为从节点异步同步,你读从节点时可能数据还没同步过来。对实时性要求高的场景(如余额查询)不适合读从节点。
javascript
// 应用端设置 Read Preference
// Node.js 驱动示例
const client = new MongoClient(uri, {
  readPreference: "secondaryPreferred"
});

// mongosh 单次查询设置
// 注意:以下为概念示意
db.orders.find().readPref("secondary");
读从节点可能拿到旧数据。如果你的业务逻辑是写后立即读(比如下单后立即查订单),务必读主节点。

04. 自动故障转移与选举

副本集能在主节点挂掉后自动选出新主,这个过程叫故障转移。选主流程基于 Raft 共识算法: 1. 从节点发现连不上主节点(心跳超时,默认 10 秒) 2. 从节点发起选举,向其他节点拉票 3. 拿到大于半数票的从节点成为新主 4. 其他节点自动从新主同步 选举优先级可以通过 priority 参数调整:priority 高的节点更容易当选主。priority 设为 0 的节点永远不会当选主(适合放在性能差的机器上做纯从库)。 votes 参数控制节点是否有投票权。hidden 参数可以把节点隐藏(客户端读不到),适合做专用的备份或分析节点。
javascript
// 查看副本集状态
rs.status();

// 查看当前配置
rs.conf();

// 调整成员优先级
const cfg = rs.conf();
cfg.members[1].priority = 2;  // 提高被选为主的概率
cfg.members[2].priority = 0;  // 永远不会当主
rs.reconfig(cfg);

05. 副本集运维要点

副本集日常运维需要注意: 1. 监控复制延迟——用 rs.printSecondaryReplicationInfo() 或 Prometheus 监控。延迟高说明从库追不上,可能影响读写分离或故障转移。 2. 定期检查 Oplog 窗口——确保 oplog 足够大,从节点即使短暂断网也能追回来。高写入场景 oplog 至少能存 24 小时的记录。 3. 备份策略——通常从从节点做备份,不影响主节点性能。但备份时要记录 oplog 位置,方便做时间点恢复。 4. 滚动升级——升级 MongoDB 版本时,先升级从节点,再切主然后升级原主。保证服务不中断。 5. 扩容——添加新节点用 rs.add(),新节点会自动初始同步(全量拷贝加增量 oplog)。初始同步期间新节点不可用。
javascript
// 添加新成员
rs.add("localhost:27020");

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

// 添加仲裁者
rs.addArb("localhost:27021");

// 手动降主(维护前把主切走)
rs.stepDown();
rs.stepDown() 让当前主节点主动退位,触发选举。做维护操作前的标准流程——先降主再操作。

知识测验

1/5正确 0

副本集最少需要几个节点?

下一节

分片集群

下一节