MongoDB 副本集(Replica Set)搭建与运维完整指南
MongoDB 副本集(Replica Set)搭建与运维完整指南
一、副本集核心概念
1.1 什么是副本集
副本集是 MongoDB 官方的高可用集群方案,由多台 MongoDB 实例组成,数据全量同步,具备自动故障转移、读写分离能力。
-
对应 MySQL 的「主从集群」,但自带自动选举、故障自愈,无需额外中间件。
-
所有节点数据完全一致,不是分片拆分数据(分片是另一种集群)。
1.2 核心角色
| 角色 | 说明 |
| ------------------ | --------------------------------------- |
| Primary(主节点) | 唯一接收写入操作的节点,所有增删改都必须走主节点;同时也可提供读服务 |
| Secondary(从节点) | 实时同步主节点数据,分担读压力;主节点宕机后可参与选举成为新主 |
| Arbiter(仲裁节点) | 不存数据,只参与投票选举,用于凑齐奇数节点,节省服务器资源(不推荐核心业务用) |
1.3 核心机制:oplog 操作日志
-
全称
operations log,是一个固定大小的环形集合,记录所有数据写入操作(增、删、改)。 -
每个节点本地都保存一份 oplog,不是只有主节点才有。
-
从节点持续拉取主节点的 oplog,在本地回放执行,实现数据同步。
-
选举新主时,以「谁的 oplog 最新」为核心依据,保证新主数据最完整。
1.4 为什么必须是奇数个节点
副本集选举需要「多数派(过半节点同意)」才能选出新主:
-
2 节点:挂 1 台,剩余 1 台不足半数,无法选举,集群彻底不可写。
-
3 节点:挂 1 台,剩余 2 台满足多数派,可正常选举和写入。
-
生产环境最低标准:3 节点副本集。
二、前置准备与环境规划
2.1 环境要求
-
MongoDB 版本:推荐 5.0 / 6.0 LTS 稳定版
-
操作系统:Linux(CentOS 7+/Ubuntu 20.04+)为主,Windows 仅适合本地测试
-
磁盘:数据盘建议 SSD,延迟越低同步性能越好
-
内存:建议 8G 以上,MongoDB 依赖内存缓存提升性能
2.2 服务器规划(3 节点标准方案)
以商城生产环境为例:
| 节点角色 | 内网 IP | 配置建议 | 选举优先级 |
| ------- | ------------ | --------- | ------------ |
| 主节点(高配) | 192.168.1.10 | 8C16G 高性能 | priority: 10 |
| 从节点 1 | 192.168.1.11 | 4C8G | priority: 1 |
| 从节点 2 | 192.168.1.12 | 4C8G | priority: 1 |
优先级作用:高配节点宕机恢复后,数据同步完成会自动抢回主节点身份。
2.3 网络与端口要求
-
默认端口:
27017,所有节点之间内网互通。 -
防火墙放行:三台机器互相开放 27017 端口(仅内网,禁止公网直连)。
-
禁止使用
127.0.0.1绑定地址,否则跨机器无法通信。 -
生产环境必须开启身份认证 + 副本集密钥文件,防止非法节点加入。
三、MongoDB 安装与基础配置
3.1 安装 MongoDB(以 Linux 为例)
-
配置官方 yum/apt 源,安装稳定版:
# CentOS 示例 cat > /etc/yum.repos.d/mongodb-org-6.0.repo << EOF [mongodb-org-6.0] name=MongoDB Repository baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/6.0/x86_64/ gpgcheck=1 enabled=1 gpgkey=https://www.mongodb.org/static/pgp/server-6.0.asc EOF yum install -y mongodb-org -
默认文件路径:
-
配置文件:
/etc/mongod.conf -
数据目录:
/var/lib/mongo -
日志目录:
/var/log/mongodb3.2 核心配置文件详解
/etc/mongod.conf是 MongoDB 唯一核心配置文件,以下是生产级标准配置,逐行解释:# 网络配置 net: port: 27017 bindIp: 0.0.0.0 # 允许所有内网IP访问,生产建议指定具体内网IP段 maxIncomingConnections: 2000 # 最大连接数 # 存储配置 storage: dbPath: /var/lib/mongo # 数据文件存放路径 journal: enabled: true # 开启日志,断电不丢数据 wiredTiger: engineConfig: cacheSizeGB: 8 # 内存缓存大小,建议设为物理内存的一半 # 日志配置 systemLog: destination: file logAppend: true path: /var/log/mongodb/mongod.log # 日志文件路径 # ====== 开启副本集的关键配置 ====== replication: replSetName: "mall_rs" # 副本集名称,所有节点必须完全一致 oplogSizeMB: 10240 # oplog 大小,单位MB,建议设为磁盘的5%~10%关键说明:
- 不写
replication.replSetName= 单机模式,没有副本集能力,也不会生成 oplog。
oplogSizeMB太小会导致宕机恢复时日志被覆盖,无法增量同步,只能全量重传。
3.3 启动与停止命令
# 启动服务 systemctl start mongod # 设置开机自启 systemctl enable mongod # 查看状态 systemctl status mongod # 重启服务 systemctl restart mongod - 不写
四、场景一:本地测试环境搭建(单机器多端口模拟)
适合本地开发调试,一台机器启动 3 个 MongoDB 实例,模拟 3 节点副本集。
4.1 创建数据与日志目录
mkdir -p /data/mongo_rs/{n1,n2,n3}
mkdir -p /data/mongo_rs/log
4.2 编写 3 份配置文件
分别创建 mongod_n1.conf、mongod_n2.conf、mongod_n3.conf,仅端口和数据目录不同,副本集名一致。
示例 mongod_n1.conf:
net:
port: 27017
bindIp: 127.0.0.1
storage:
dbPath: /data/mongo_rs/n1
journal:
enabled: true
systemLog:
destination: file
logAppend: true
path: /data/mongo_rs/log/n1.log
replication:
replSetName: "mall_rs"
oplogSizeMB: 1024
n2 端口改 27018,路径改 n2;n3 端口改 27019,路径改 n3。
4.3 分别启动 3 个实例
mongod -f /data/mongo_rs/mongod_n1.conf
mongod -f /data/mongo_rs/mongod_n2.conf
mongod -f /data/mongo_rs/mongod_n3.conf
4.4 初始化副本集
连接其中一个节点(比如 27017):
mongosh --port 27017
执行初始化命令:
rs.initiate({
_id: "mall_rs", // 必须和配置文件的 replSetName 完全一致
members: [
{ _id: 0, host: "127.0.0.1:27017", priority: 10 },
{ _id: 1, host: "127.0.0.1:27018", priority: 1 },
{ _id: 2, host: "127.0.0.1:27019", priority: 1 }
]
})
返回 { ok: 1 } 表示初始化成功。
4.5 验证集群状态
// 查看集群状态,确认谁是 PRIMARY
rs.status()
// 查看完整配置
rs.conf()
// 查看 oplog 信息
rs.printReplicationInfo()
-
状态字段
stateStr为PRIMARY表示主节点,SECONDARY表示从节点。 -
初始化完成后,命令行前缀会自动变成
mall_rs [primary]>或mall_rs [secondary]>。
五、场景二:生产环境多机部署
5.1 每台机器修改配置文件
三台机器分别修改 /etc/mongod.conf,保证:
-
replSetName: "mall_rs"完全相同; -
bindIp允许内网访问; -
oplogSizeMB设置合理(建议 10G 以上)。修改完成后,三台机器分别启动 MongoDB:
systemctl start mongod systemctl enable mongod5.2 初始化副本集(在任意一台执行即可)
登录 192.168.1.10 的 MongoDB:
mongosh --host 192.168.1.10 --port 27017执行初始化:
rs.initiate({ _id: "mall_rs", members: [ { _id: 0, host: "192.168.1.10:27017", priority: 10 }, { _id: 1, host: "192.168.1.11:27017", priority: 1 }, { _id: 2, host: "192.168.1.12:27017", priority: 1 } ] })初始化成功后,配置会自动同步到另外两台节点,无需重复操作。
5.3 生产安全加固(必做)
步骤1:创建管理员用户
在主节点执行:
use admin db.createUser({ user: "admin", pwd: "你的强密码", roles: [ { role: "root", db: "admin" } ] })步骤2:生成副本集密钥文件
密钥用于节点之间内部认证,防止非法节点混入集群。
# 生成密钥文件 openssl rand -base64 756 > /data/mongo_keyfile chmod 400 /data/mongo_keyfile chown mongod:mongod /data/mongo_keyfile将该文件完全相同地复制到三台机器的相同路径。
步骤3:配置文件开启认证
三台机器的
mongod.conf追加:security: authorization: enabled keyFile: /data/mongo_keyfile重启三台 MongoDB 服务生效。
六、副本集核心配置与管理
6.1 调整选举优先级
让高配机器宕机恢复后自动变回主节点。
// 1. 获取当前配置
cfg = rs.conf()
// 2. 修改第0个节点(192.168.1.10)的优先级
cfg.members[0].priority = 10
// 3. 应用配置
rs.reconfig(cfg)
注意:
- priority 取值 0~1000,数值越高越容易当选主节点。
- priority = 0 的节点永远不会成为主节点,适合纯备份、报表节点。
- 旧主恢复后,必须先追平 oplog 数据,才会触发重新选举。
6.2 手动切换主节点
维护主节点时,手动让当前主节点退位:
// 让当前主节点退位 300 秒,期间不会重新参选
rs.stepDown(300)
执行后集群会自动选出新主,高优先级节点会优先当选。
6.3 新增 / 移除节点
// 新增从节点
rs.add("192.168.1.13:27017")
// 新增仲裁节点
rs.addArb("192.168.1.14:27017")
// 移除节点
rs.remove("192.168.1.13:27017")
6.4 调整 oplog 大小
运行中也可在线调整,无需重启:
use local
db.adminCommand({ replSetResizeOplog: 1, size: 20480 }) // 单位MB,设为20G
七、Node.js + Mongoose 连接副本集
7.1 完整连接代码
const mongoose = require('mongoose');
// 副本集连接地址:所有节点IP都写上,指定 replicaSet 名称
const uri = "mongodb://admin:密码@192.168.1.10:27017,192.168.1.11:27017,192.168.1.12:27017/mall?replicaSet=mall_rs&authSource=admin";
mongoose.connect(uri, {
maxPoolSize: 100, // 连接池大小
readPreference: "secondaryPreferred", // 默认读偏好:优先从从节点读
writeConcern: { w: "majority" } // 写入安全:多数节点确认才返回成功
});
const db = mongoose.connection;
db.on('error', console.error.bind(console, '连接错误:'));
db.once('open', () => {
console.log('MongoDB 副本集连接成功');
});
7.2 读写偏好(readPreference)详解
| 模式 | 说明 | 适用场景 |
| -------------------- | ----------- | ------------- |
| primary | 只从主节点读 | 强一致要求、写后立刻查 |
| primaryPreferred | 优先主节点,主挂了读从 | 要求一致性但兼顾可用性 |
| secondary | 只从从节点读 | 非实时统计、报表、历史数据 |
| secondaryPreferred | 优先从节点,从挂了读主 | 默认推荐,兼顾性能与可用性 |
7.3 写后立刻查:强制读主库最佳实践
这是解决主从延迟、查不到刚插入数据的标准方案。
const Order = mongoose.model('Order', orderSchema);
// 1. 写入订单(自动走主节点)
const newOrder = await Order.create({
userId: 1001,
amount: 99.9,
status: 'pending'
});
// 2. 强制读主节点,保证一定能查到刚写入的数据
const orderDetail = await Order.findById(newOrder._id).read('primary');
// 3. 普通历史列表查询,自动走从节点(全局默认 secondaryPreferred)
const orderList = await Order.find({ userId: 1001 }).sort({ createTime: -1 });
7.4 写入安全级别 writeConcern
| 级别 | 说明 | 适用场景 |
| --------------- | -------------- | --------------- |
| w: 1 | 主节点写入成功就返回(默认) | 性能优先,可接受极小概率丢数据 |
| w: "majority" | 多数节点写入成功才返回 | 订单、支付等核心数据,宕机不丢 |
| w: 0 | 不等待确认,发出去就完事 | 日志、埋点等可丢失数据 |
商城订单、支付、用户余额建议统一使用
w: "majority"。
八、读写分离业务规范(直接落地)
8.1 必须强制读主(.read('primary'))的场景
-
新增数据后立即回显详情(下单成功页、创建后详情)
-
更新数据后立即校验状态(支付回调、退款后刷新、取消订单)
-
事务内的所有查询
-
库存、余额、金额等强一致性数据查询
-
后台管理系统刚提交修改后的详情查询
8.2 推荐读从库的场景
-
用户历史订单列表、分页查询
-
商品列表、详情页(非实时库存)
-
数据统计、报表导出
-
日志、埋点、行为记录查询
-
大数据分析、离线计算
九、故障模拟与恢复流程
9.1 从节点宕机与恢复
现象:主节点正常,业务写入不受影响;读请求自动避开故障节点。
恢复流程:
-
修复故障机器,启动 MongoDB 服务;
-
节点自动连接集群,识别当前主节点;
-
对比本地 oplog 位点,拉取宕机期间缺失的日志,增量回放;
-
数据追平后自动恢复为 SECONDARY,重新承接读请求。
注意:如果宕机太久,oplog 已被覆盖,节点会自动触发全量重同步。
9.2 主节点宕机与自动选举
现象:
-
剩余从节点通过心跳检测到主节点失联(默认 10 秒超时);
-
存活节点发起投票,选出 oplog 最新、优先级最高的节点成为新主;
-
Mongoose 驱动自动感知新主,写入请求无缝切换。
业务影响:选举期间(几秒内)不可写入,选举完成后自动恢复。
9.3 旧主节点恢复后的自动切回
-
旧主修复启动,发现集群已有新主,自动降级为 SECONDARY;
-
拉取新主的 oplog,追平所有宕机期间的数据;
-
因为自身 priority 更高,数据同步完成后自动触发重新选举;
-
重新成为 PRIMARY,业务写入自动切回高配机器。
9.4 极端场景:oplog 覆盖后的全量重同步
如果从节点宕机时间超过 oplog 保留时长,无法增量同步,执行手动重同步:
# 1. 停止故障节点 MongoDB # 2. 清空数据目录 rm -rf /var/lib/mongo/* # 3. 启动 MongoDB,节点会自动从主节点全量复制数据 systemctl start mongod
十、运维注意事项与最佳实践
10.1 数据一致性保障
-
核心业务写入开启
w: "majority",杜绝宕机丢数据; -
写后实时查询强制读主,避免延迟导致业务 bug;
-
禁止直接向从节点写入数据(MongoDB 默认拒绝,但要防止权限误配)。
10.2 核心监控指标
-
副本集状态:节点数量、主节点是否存在、节点是否健康
-
复制延迟:主从 oplog 时间差,正常应在 1 秒内
-
oplog 窗口:oplog 可保留的时长,建议至少 24 小时
-
连接数、内存使用率、磁盘使用率
10.3 备份策略
-
每日全量备份 + 实时 oplog 备份,支持任意时间点恢复
-
备份从从节点执行,不影响主库性能
-
定期演练备份恢复流程
10.4 扩容原则
-
读压力大:增加从节点数量
-
写压力/数据量过大:升级为分片集群(Sharded Cluster)
-
节点数量保持奇数,避免脑裂
十一、常见问题排查
11.1 节点无法加入集群
-
检查
replSetName是否完全一致(大小写敏感) -
检查网络互通、端口是否放行
-
检查密钥文件权限是否为 400、内容是否完全一致
-
检查 MongoDB 版本是否一致
11.2 从节点无法读取数据
-
MongoDB 从节点默认拒绝读请求,需通过驱动设置
readPreference -
Shell 中测试需执行
rs.slaveOk()或db.getMongo().setReadPref('secondary')11.3 同步延迟过高
-
从节点硬件配置过低,CPU/磁盘 IO 跟不上
-
主节点大批量写入、大事务操作
-
网络带宽不足、延迟高
-
oplog 太小,频繁循环覆盖
11.4 选举失败
-
存活节点不足半数
-
所有节点时间不同步(建议配置 NTP 时间同步)
-
节点之间网络分区,无法互相通信
十二、关键避坑总结
-
默认启动是单机:必须配置
replSetName才是副本集,才有 oplog 和自动选举。 -
节点数量必须奇数:2 台机器无法容错,最低标准 3 节点。
-
延迟必然存在:任何主从架构都有延迟,靠强制读主解决写后查问题,不是靠框架自动处理。
-
高配机器设高 priority:宕机恢复后自动切回主节点,不用手动干预。
-
生产必须开认证:禁止裸奔部署,密钥文件 + 用户认证双保险。
本文由萧兮的博客原创发布,欢迎转载,转载务必保留原文链接。
萧兮的博客:https://www.20010515.xyz · 原文:https://www.20010515.xyz/posts/019ef296-2194-7401-9dce-fb96d6554067