創建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();