MongoDB复制集是一组维护相同数据集的mongod进程组成的集群,核心优势在于主节点故障时能自动选举新主节点,避免服务长时间中断。搭建复制集前需要先理解三个关键角色:主节点负责接收写入,从节点异步复制oplog,仲裁节点只参与选举不存储数据。本文会从目录规划、配置文件编写到初始化命令逐步演示,重点说明同一台机器上如何通过端口和dbpath隔离多个实例,以及rs.initiate和rs.add的正确用法。整个过程不依赖特定云平台,本地虚拟机或物理机均可完成。

一、复制集的核心概念与目录规划
复制集中的每个节点本质上都是一个独立的mongod进程,它们通过配置项replication.replSetName声明自己属于同一个复制集。只有名称完全一致的多个实例才能互相发现并建立心跳连接。如果名称拼写不一致,节点会一直处于RECOVERING或REMOVED状态,这是初学者最常遇到的错误。
角色方面,主节点负责处理所有写请求,并将写入记录到oplog。从节点通过拉取主节点的oplog来保持数据同步,默认情况下从节点不响应读请求,需要执行rs.secondaryOk或者设置readPreference。仲裁节点不复制数据,它的作用是在偶数节点下提供投票权,避免选举时出现平票。生产环境通常推荐奇数个数据节点,而不是依赖仲裁节点。
目录规划建议为每个实例创建独立的数据目录和日志文件。以Linux为例,可以创建/data/mongodb/rs0-0、/data/mongodb/rs0-1、/data/mongodb/rs0-2三个目录。如果使用Windows系统,路径可以写作C:\data\mongodb\rs0-0,注意反斜杠不能省略。每个实例还需要独立的日志路径,方便出问题时查看。
# 节点1配置文件示例 systemLog: destination: file path: /data/mongodb/rs0-0/mongod.log logAppend: true storage: dbPath: /data/mongodb/rs0-0 net: port: 27017 bindIp: 0.0.0.0 replication: replSetName: rs0
上面配置中replSetName统一为rs0,三个节点都使用相同的值。dbPath和port必须不同,否则实例会因端口占用或数据目录锁冲突而启动失败。bindIp设置为0.0.0.0是为了方便远程连接,生产环境应根据网络策略收紧。
二、启动三个mongod实例并初始化复制集
新建三个配置文件node1.conf、node2.conf和node3.conf,分别修改port为27017、27018、27019,修改dbPath和日志路径。然后在终端中依次执行mongod --config命令启动。如果使用的是较老的mongo shell,后续命令用mongo代替mongosh;新版本推荐使用mongosh。
mongod --config /data/mongodb/conf/node1.conf mongod --config /data/mongodb/conf/node2.conf mongod --config /data/mongodb/conf/node3.conf
三个进程启动后,连接到其中一个实例作为初始主节点。例如连接27017端口:mongosh --port 27017。然后执行rs.initiate,传入一个包含_id和members的配置对象。members数组中的每个成员通过_id标识,host字段填写主机名和端口。如果三个节点都在同一台机器上,host可以写localhost:27017、localhost:27018、localhost:27019。
rs.initiate({
_id: 'rs0',
members: [
{ _id: 0, host: 'localhost:27017' },
{ _id: 1, host: 'localhost:27018' },
{ _id: 2, host: 'localhost:27019' }
]
})
初始化完成后,mongosh提示符会从独立的shell变为rs0:PRIMARY或者rs0:SECONDARY。也可以用rs.status查看各节点状态。health为1表示节点正常,stateStr为PRIMARY、SECONDARY或ARBITER表示当前角色。如果某个节点一直显示STARTUP或RECOVERING,通常是因为replSetName不一致或网络不通。
如果初始化时只加入了一个节点,后续可以通过rs.add('localhost:27018')逐个添加。rs.add会在主节点上执行,新加入的节点会自动从主节点拉取全量数据。添加完成后,用rs.conf查看最终配置,确保members数量正确。
三、验证数据复制与自动故障转移
在主节点上创建测试数据库和集合,插入几条文档。然后连接到从节点,默认情况下从节点会拒绝读操作并提示not master and slaveOk=false。需要执行db.getMongo().setReadPref('secondaryPreferred')或者直接rs.secondaryOk()来允许从节点读取。此时再查询集合,应该能看到主节点写入的数据。
// 在主节点执行
use shop
db.orders.insertMany([
{ item: 'keyboard', price: 199 },
{ item: 'mouse', price: 89 }
])
// 在从节点执行
rs.secondaryOk()
use shop
db.orders.find()
故障转移测试可以通过直接终止主节点进程来实现。假设27017当前是主节点,使用kill命令结束该进程。复制集会在几秒内完成选举,从两个从节点中选出一个新的主节点。旧主节点恢复后,会以从节点身份重新加入复制集,并从新主节点同步自己宕机期间错过的数据。
选举过程依赖心跳和优先级配置。默认情况下所有数据节点优先级都是1,按最新oplog时间选择候选。如果希望某个节点永远不成为主节点,可以在配置中设置priority为0。隐藏节点可以设置hidden: true配合priority: 0,用于专用备份或报表查询。
四、生产环境配置与常见问题
本地测试时bindIp可以设置成0.0.0.0,但生产环境应该绑定内网IP,并通过防火墙限制只有集群节点和应用服务器可以访问对应端口。MongoDB默认不开启认证,这会导致任何能访问端口的人都可以操作数据。开启认证前需要先创建超级用户,并生成keyfile供复制集节点之间认证。
openssl rand -base64 756 > /data/mongodb/keyfile chmod 400 /data/mongodb/keyfile
keyfile内容需要完全相同,权限必须为400或600,否则mongod会因权限过于宽松拒绝启动。配置项security.keyFile指定keyfile路径后,复制集节点之间的通信会自动启用内部认证。外部客户端连接时则需要提供用户名和密码。
另一个常见问题是oplog大小不足。oplog是固定大小的集合,如果写入量很大而oplog太小,从节点可能无法追上主节点,最终需要重新全量同步。可以通过replSetResizeOplog命令调整oplog大小,或者提前在配置文件中设置oplogSizeMB。监控上建议定期执行rs.printReplicationInfo检查oplog窗口,确认窗口时间大于备份和恢复所需时间。
复制集搭建完成后,还应该关注节点分布。如果所有节点都部署在同一台物理机或同一个机架上,发生硬件故障时整个复制集都会不可用。至少将三个节点分散到不同的故障域,例如不同的虚拟机宿主机或可用区。这样主节点故障后,剩余的从节点仍然可以完成选举并提供读写服务。

