创建Mongodb的副本集最好是新建一个文件夹,如D:/data,不要在mongodb安装文件夹里面创建副本集,虽然这样也可以,但是容易造成误操作或路径混乱;在新建文件夹里与现有 MongoDB 数据隔离,避免误操作影响原有数据,后续要清理和迁移都要方便得多。
1、在cmd命令符中创建三个数据文件夹
mkdir D:\data\db1 # 主节点(Primary)
mkdir D:\data\db2 # 从节点1(Secondary)
mkdir D:\data\db3 # 从节点2(Secondary)
2、启动 3 个 mongod 实例(模拟 3 节点副本集)
打开 3 个独立的 CMD 窗口,分别运行:
窗口 1(主节点,端口 27017)
mongod --replSet rs0 --dbpath D:\data\db1 --port 27017 --logpath D:\data\db1\log.log --bind_ip 0.0.0.0
窗口 2(从节点1,端口 27018)
mongod --replSet rs0 --dbpath D:\data\db2 --port 27018 --logpath D:\data\db2\log.log --bind_ip 0.0.0.0
窗口 3(从节点2,端口 27019)
mongod --replSet rs0 --dbpath D:\data\db3 --port 27019 --logpath D:\data\db3\log.log --bind_ip 0.0.0.0
注意:这三个cmd窗口都不能关闭,如果关闭则副本集也会关闭,他们会没啥反应,只能在mongosh中才能查看到状态
3、 初始化副本集
连接主节点(27017)并执行初始化:
再打开一个cmd命令,首先你要安装好mongodb shell,下面的mongosh命令才能用
这里先登陆主节点
mongosh --port 27017
然后初始化副本集,注意在cmd命令中不能换行书写的,你最好写成一行,要么用mongosh加载js文件
rs.initiate({_id: "rs0",members: [{ _id: 0, host: "localhost:27017" },{ _id: 1, host: "localhost:27018" },{ _id: 2, host: "localhost:27019" }]
})
用mongosh加载js文件初始化,把上面的写在init_replica.js文件后用mongosh加载
mongosh --port 27017 --file init_replica.js
初始化成功后,验证各节点状态,进入主节点
mongosh --port 27017
rs.status() // 检查各节点状态
rs.isMaster() // 查看主节点信息
现在你就可以测试了,比如在主节点中插入数据,然后在从节点中查询
db.user.insertOne({"name":"Bob",age:22});
登陆从节点27018
mongosh --port 27018
db.user.find({});//这时会显示主节点插入的数据
这里的副本集和你单机的mongodb数据库是一模一样的,在主节点上也有admin,local,test等集合,一样可以设置用户名和密码及ssl证书
4、创建管理员账户和从节点管理
在初始化后我们就可以进行创建用户名和密码,上面都是直接登陆,这样显然是不安全的,进入我们的主节点
use admin
db.createUser({user: "admin",pwd: "yourSecurePassword", // 替换为强密码roles: [{ role: "root", db: "admin" }, // 超级管理员{ role: "clusterAdmin", db: "admin" } // 副本集管理权限]
});
说明:用户权限在副本集中的同步机制,主节点创建的用户会自动同步到所有从节点
MongoDB 的 用户数据存储在 admin 数据库 中,而 admin 数据库的内容(包括用户账号)会通过副本集的 Oplog 自动同步到所有成 员。
因此:
只需在主节点创建用户(如 admin),从节点会自动同步该用户信息。
从节点不需要单独创建用户,但必须用相同的用户名/密码登录。
编辑 MongoDB 配置文件(通常位于 /etc/mongod.conf 或 C:\Program Files\MongoDB\Server<version>\bin\mongod.cfg):
security:authorization: enabled # 启用认证keyFile: /path/to/keyfile # 副本集内部认证文件(下一步生成)
- 生成 KeyFile(副本集内部认证)
openssl rand -base64 756 > /path/to/mongo-keyfile
chmod 400 /path/to/mongo-keyfile # 限制权限
KeyFile 的使用规则
所有副本集成员必须使用完全相同的 KeyFile 文件
KeyFile 是副本集成员之间互相认证的共享密钥,内容必须一致。
生成后需严格限制权限(Linux: chmod 400,Windows: 仅管理员可读)。
将 keyfile 复制到所有节点的相同路径。
- 重启所有节点
mongod --replSet rs0 --dbpath /data/db1 --port 27017 --bind_ip 0.0.0.0 --auth --keyFile /path/to/mongo-keyfile
(其他节点同理)
- 验证认证
mongosh --port 27017 -u admin -p yourSecurePassword --authenticationDatabase adminrs.status() // 确认副本集状态正常
配置项作用
authorization: enabled 强制所有连接必须提供用户名/密码
keyFile 副本集成员间通信的共享密钥,防止未授权节点加入
clusterAdmin 角色 允许用户管理副本集(如 rs.status(), rs.reconfig())
root 角色 超级管理员权限(谨慎分配)
生产环境最佳实践
避免使用默认端口:修改 27017 为非常用端口。
网络隔离:将副本集部署在内网,仅暴露主节点给应用服务器。
定期轮换 KeyFile:每 3 个月更新一次 keyfile。
审计日志:启用 auditLog 记录所有敏感操作。
强制关闭节点
db.shutdownServer() // 主节点才能执行
// 或强制关闭(从节点适用)
db.adminCommand({ shutdown: 1, force: true })
如果要重新初始化
停止 MongoDB:
net stop MongoDB
(或 taskkill /F /IM mongod.exe)
删除数据目录:
rmdir /s /q D:\data\db1
rmdir /s /q D:\data\db2
rmdir /s /q D:\data\db3
重新创建目录:
mkdir D:\data\db1
mkdir D:\data\db2
mkdir D:\data\db3
再进行最上面的步骤
测试用Node.js连接副本集
//mongotest.js
const {MongoClient}=require('mongodb');//这里我重新建了一个只能读写的用户名
//employees是数据库名,replicaSet=rs0表示连接到副本集rs0
//authSource=employees表示存储登陆用户名和密码的数据库
//w=majority表示保证写入操作传到大多数从节点,说人话就是大多数从节点复制了这个写入的数据wtimeoutMs表示延时
const uri='mongodb://qqtest:123456@localhost:27018,localhost:27019,localhost:27201/employees?replicaSet=rs0&authSource=employees&w=majority&wtimeoutMS=5000';
const client=new MongoClient(uri);async function run(){try{await client.connect();console.log('Connected to MongoDB replica set');const db=client.db('employees');const collection=db.collection('users');const result=await collection.insertMany([{"name":"Bob",age:25},{'name':"Alice",age:32},{"writeConcern":{'w':'majority','wtimeoutMS':5000}} //也可以把大多数写入从节点写在这里]);console.log("文档已经插入:",result.insertedIds);const findResult =await collection.find({'name':'kimi'}).toArray();console.log('查询到文档:',findResult);}catch(err){console.error('Error:',err);}finally{await client.close();console.log('Connection closed');}
}run();