環(huán)境準(zhǔn)備
服務(wù)器:centOS7
mongodb版本:3.6.2
副本集方案:1個(gè)主節(jié)點(diǎn)+2個(gè)二級(jí)節(jié)點(diǎn)
注意:部署生產(chǎn)環(huán)境的時(shí)候最好將不同的節(jié)點(diǎn)部署在不同的服務(wù)器上,本文為了演示,因此直接部署在同一臺(tái)。
配置
3個(gè)節(jié)點(diǎn)除了端口號(hào)其余的配置都一樣,如下:
processManagement:
fork: true
pidFilePath: ../mongod.pid
net:
bindIp: 0.0.0.0
port: 27019
maxIncomingConnections: 65536
wireObjectCheck: true
ipv6: false
storage:
dbPath: ../data
indexBuildRetry: true
journal:
enabled: true
systemLog:
path: ../logs/mongodb.log
logAppend: true
destination: file
replication:
oplogSizeMB: 10240
replSetName: myrepl
secondaryIndexPrefetch: all
security:
authorization: enabled
clusterAuthMode: keyFile
keyFile: ../../mongodb.key
javascriptEnabled: true
步驟
- 生成keyFile文件
openssl rand -base64 735 > mongodb.key
chmod 600 mongodb.key
- 分別啟動(dòng)各個(gè)節(jié)點(diǎn)mongodb實(shí)例
./mongod -f ../mongod.conf//進(jìn)入mongodb的bin目錄下
- 服務(wù)器啟動(dòng)之后,進(jìn)入任意一個(gè)節(jié)點(diǎn)的命令行,將三個(gè)的實(shí)例關(guān)聯(lián)起來(lái)
> config = {
... _id : "myrepl",
... members : [
... {_id : 0, host : "127.0.0.1:27017"},
... {_id : 1, host : "127.0.0.1:27018"},
... {_id : 2, host : "127.0.0.1:27019"}]}
{
"_id" : "myrepl",
"members" : [
{
"_id" : 0,
"host" : "127.0.0.1:27017"
},
{
"_id" : 1,
"host" : "127.0.0.1:27018"
},
{
"_id" : 2,
"host" : "127.0.0.1:27019"
}
]
}
- 初始化副本集的配置
> rs.initiate(config)
{
"ok" : 1,
"operationTime" : Timestamp(1520260635, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1520260635, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
>
- 當(dāng)初始化配置信息后,可以明顯的看到mongodb的命令行發(fā)生了變化,會(huì)顯示出當(dāng)前節(jié)點(diǎn)所屬的副本集名稱和節(jié)點(diǎn)類型。
[root@pmj bin]# ./mongo
MongoDB shell version v3.6.2
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.6.2
myrepl:PRIMARY>
至此,mongodb的副本集配置已經(jīng)完成了,接下來(lái)是測(cè)試副本集是否可用
測(cè)試
- 由于之前的配置是開(kāi)啟安全認(rèn)證的,因此需要進(jìn)入主節(jié)點(diǎn)先建一個(gè)root用戶,然后認(rèn)證
myrepl:PRIMARY> use admin
switched to db admin
myrepl:PRIMARY> db.createUser({user: "admin",pwd:"admin",roles:[{role:"root",db:"admin"}]})
Successfully added user: {
"user" : "admin",
"roles" : [
{
"role" : "root",
"db" : "admin"
}
]
}
myrepl:PRIMARY> db.auth("admin","admin")
1
myrepl:PRIMARY>
- 查看副本集狀態(tài)
myrepl:PRIMARY> rs.config()
{
"_id" : "myrepl",
"version" : 1,
"protocolVersion" : NumberLong(1),
"members" : [
{
"_id" : 0,
"host" : "127.0.0.1:27017",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {
},
"slaveDelay" : NumberLong(0),
"votes" : 1
},
{
"_id" : 1,
"host" : "127.0.0.1:27018",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {
},
"slaveDelay" : NumberLong(0),
"votes" : 1
},
{
"_id" : 2,
"host" : "127.0.0.1:27019",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {
},
"slaveDelay" : NumberLong(0),
"votes" : 1
}
],
"settings" : {
"chainingAllowed" : true,
"heartbeatIntervalMillis" : 2000,
"heartbeatTimeoutSecs" : 10,
"electionTimeoutMillis" : 10000,
"catchUpTimeoutMillis" : -1,
"catchUpTakeoverDelayMillis" : 30000,
"getLastErrorModes" : {
},
"getLastErrorDefaults" : {
"w" : 1,
"wtimeout" : 0
},
"replicaSetId" : ObjectId("5a9d561bdecc034b55b5a5b9")
}
}
- 查看主節(jié)點(diǎn)信息
myrepl:PRIMARY> rs.isMaster()
{
"hosts" : [
"127.0.0.1:27017",
"127.0.0.1:27018",
"127.0.0.1:27019"
],
"setName" : "myrepl",
"setVersion" : 1,
"ismaster" : true,
"secondary" : false,
"primary" : "127.0.0.1:27017",
"me" : "127.0.0.1:27017",
//.......................
}
- 新建一個(gè)測(cè)試庫(kù)mydb和測(cè)試用戶test,用于測(cè)試數(shù)據(jù)插入
myrepl:PRIMARY> use mydb
switched to db mydb
myrepl:PRIMARY> db.createUser({user: "test",pwd:"test",roles:[{role:"readWrite",db:"mydb"}]})
Successfully added user: {
"user" : "test",
"roles" : [
{
"role" : "readWrite",
"db" : "mydb"
}
]
}
myrepl:PRIMARY> use mydb
switched to db mydb
myrepl:PRIMARY> db.auth("test","test")
1
5.插入100條數(shù)據(jù)
myrepl:PRIMARY> for(var i = 0; i < 100; i++) {
... db.testCollection.insert({order: i, name: "test" + i}) }
WriteResult({ "nInserted" : 1 })
myrepl:PRIMARY> db.testCollection.count()
100
- 進(jìn)入二級(jí)節(jié)點(diǎn),查看數(shù)據(jù)是否同步
myrepl:SECONDARY> use mydb
switched to db mydb
myrepl:SECONDARY> db.auth("test","test")
1
myrepl:SECONDARY> db.testCollection.count()
2018-03-05T22:58:27.069+0800 E QUERY [thread1] Error: count failed: {
"operationTime" : Timestamp(1520261897, 1),
"ok" : 0,
"errmsg" : "not master and slaveOk=false",
"code" : 13435,
"codeName" : "NotMasterNoSlaveOk",
//.........................省略
}
當(dāng)我們要查看二級(jí)節(jié)點(diǎn)數(shù)據(jù)時(shí),發(fā)現(xiàn)出錯(cuò),這是因?yàn)槎?jí)節(jié)點(diǎn)默認(rèn)情況下是拒絕讀取的,因此需開(kāi)啟讀取功能
myrepl:SECONDARY> conn = new Mongo("127.0.0.1:27018")
connection to 127.0.0.1:27018
myrepl:SECONDARY> conn.setSlaveOk()
接著再查看數(shù)據(jù),發(fā)現(xiàn)已經(jīng)同步了
myrepl:SECONDARY> use mydb
switched to db mydb
myrepl:SECONDARY> db.auth("test","test")
1
myrepl:SECONDARY> db.testCollection.count()
100
故障轉(zhuǎn)移
在這個(gè)例子中,副本集有3個(gè)成員,因此,我們將現(xiàn)在的主節(jié)點(diǎn)(端口號(hào)為27017)進(jìn)程殺死,然后再查看副本集的狀態(tài),發(fā)現(xiàn)節(jié)點(diǎn)(端口號(hào)27019)的變?yōu)橹鞴?jié)點(diǎn)
myrepl:PRIMARY> rs.isMaster()
{
"hosts" : [
"127.0.0.1:27017",
"127.0.0.1:27018",
"127.0.0.1:27019"
],
"setName" : "myrepl",
"setVersion" : 1,
"ismaster" : true,
"secondary" : false,
"primary" : "127.0.0.1:27019",
"me" : "127.0.0.1:27019",
//后面省略
測(cè)試數(shù)據(jù)插入也是正常的
myrepl:PRIMARY> use mydb
switched to db mydb
myrepl:PRIMARY> db.testCollection.count()
100
myrepl:PRIMARY> db.testCollection.insert({name:"test",pwd:"test",address:"beijing"})
WriteResult({ "nInserted" : 1 })
將最開(kāi)始關(guān)掉進(jìn)程的節(jié)點(diǎn)重啟,會(huì)發(fā)現(xiàn)其變?yōu)楦北炯亩?jí)節(jié)點(diǎn),且數(shù)據(jù)會(huì)同步
增刪節(jié)點(diǎn)
- 刪除節(jié)點(diǎn):進(jìn)入主節(jié)點(diǎn)27019,然后移除27018節(jié)點(diǎn)
myrepl:PRIMARY> use admin
switched to db admin
myrepl:PRIMARY> db.auth("admin","admin")
1
myrepl:PRIMARY> rs.remove("127.0.0.1:27018")
{
"ok" : 1,
"operationTime" : Timestamp(1520340394, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1520340394, 1),
"signature" : {
"hash" : BinData(0,"0VUzGmXGZ4EXAUC/HYJbRaPY6qc="),
"keyId" : NumberLong("6529469760260800513")
}
}
}
myrepl:PRIMARY> rs.isMaster()
{
"hosts" : [
"127.0.0.1:27017",
"127.0.0.1:27019"
],
"setName" : "myrepl",
"setVersion" : 2,
"ismaster" : true,
"secondary" : false,
"primary" : "127.0.0.1:27019",
"me" : "127.0.0.1:27019",
//后面省略
- 增加節(jié)點(diǎn):進(jìn)入主節(jié)點(diǎn)27019,然后將剛剛刪除的節(jié)點(diǎn)27018又重新加入到集群中
myrepl:PRIMARY> rs.add("127.0.0.1:27018")
{
"ok" : 1,
"operationTime" : Timestamp(1520340538, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1520340538, 1),
"signature" : {
"hash" : BinData(0,"a3TQIbz8yCaDh4Uxh4yUUkF7auk="),
"keyId" : NumberLong("6529469760260800513")
}
}
}
myrepl:PRIMARY> rs.isMaster()
{
"hosts" : [
"127.0.0.1:27017",
"127.0.0.1:27019",
"127.0.0.1:27018"
],
"setName" : "myrepl",
"setVersion" : 3,
"ismaster" : true,
"secondary" : false,
"primary" : "127.0.0.1:27019",
"me" : "127.0.0.1:27019",
"electionId" : ObjectId("7fffffff0000000000000002"),
后面省略.......
修改節(jié)點(diǎn)
修改優(yōu)先級(jí)
- 獲取節(jié)點(diǎn)配置信息
myrepl:PRIMARY> cfg = rs.config()
{
"_id" : "myrepl",
"version" : 3,
"protocolVersion" : NumberLong(1),
"members" : [
{
"_id" : 0,
"host" : "127.0.0.1:27017",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {
},
"slaveDelay" : NumberLong(0),
"votes" : 1
},
{
"_id" : 2,
"host" : "127.0.0.1:27019",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {
},
"slaveDelay" : NumberLong(0),
"votes" : 1
},
{
"_id" : 3,
"host" : "127.0.0.1:27018",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {
},
"slaveDelay" : NumberLong(0),
"votes" : 1
}
],
//后面省略
- 修改節(jié)點(diǎn)信息
myrepl:PRIMARY> cfg.members[0].priority=9
9
myrepl:PRIMARY> cfg.members[1].priority=6
6
myrepl:PRIMARY> cfg.members[2].priority=6
6
- 重新配置副本集信息后,發(fā)現(xiàn)每個(gè)節(jié)點(diǎn)的priority值改了,并且優(yōu)先級(jí)最高的變?yōu)榱酥鞴?jié)點(diǎn)
myrepl:PRIMARY> rs.reconfig(cfg)
{
"ok" : 1,
"operationTime" : Timestamp(1520341204, 2),
"$clusterTime" : {
"clusterTime" : Timestamp(1520341204, 2),
"signature" : {
"hash" : BinData(0,"G3PlqiS3AP7NHVVqp9svYSmNrdI="),
"keyId" : NumberLong("6529469760260800513")
}
}
}
myrepl:PRIMARY> rs.config()
{
"_id" : "myrepl",
"version" : 4,
"protocolVersion" : NumberLong(1),
"members" : [
{
"_id" : 0,
"host" : "127.0.0.1:27017",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 9,
"tags" : {
},
"slaveDelay" : NumberLong(0),
"votes" : 1
},
{
"_id" : 2,
"host" : "127.0.0.1:27019",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 6,
"tags" : {
},
"slaveDelay" : NumberLong(0),
"votes" : 1
},
{
"_id" : 3,
"host" : "127.0.0.1:27018",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 6,
"tags" : {
},
"slaveDelay" : NumberLong(0),
"votes" : 1
}
],
隱藏成員
要將節(jié)點(diǎn)變?yōu)殡[藏節(jié)點(diǎn),節(jié)點(diǎn)的priority值必須為0,否則配置會(huì)失敗
myrepl:PRIMARY> cfg.members[2].priority=0
0
myrepl:PRIMARY> cfg.members[2].hidden=true
true
myrepl:PRIMARY> rs.reconfig(cfg)
{
"ok" : 1,
"operationTime" : Timestamp(1520341554, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1520341554, 1),
"signature" : {
"hash" : BinData(0,"ZHXBdgARPnus/DDNl4fVjXVs30I="),
"keyId" : NumberLong("6529469760260800513")
}
}
}
myrepl:PRIMARY> rs.isMaster()
{
"hosts" : [
"127.0.0.1:27017",
"127.0.0.1:27019"
],
"setName" : "myrepl",
"setVersion" : 5,
"ismaster" : true,
"secondary" : false,
"primary" : "127.0.0.1:27017",
"me" : "127.0.0.1:27017",
延遲節(jié)點(diǎn)
要將節(jié)點(diǎn)變?yōu)檠舆t節(jié)點(diǎn),節(jié)點(diǎn)的priority值必須為0,否則配置會(huì)失敗
myrepl:PRIMARY> cfg.members[1].priority=0
0
myrepl:PRIMARY> cfg.members[1].slaveDelay=NumberLong(240)
NumberLong(60000)
myrepl:PRIMARY> rs.reconfig(cfg)
{
"ok" : 1,
"operationTime" : Timestamp(1520341773, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1520341773, 1),
"signature" : {
"hash" : BinData(0,"6dkgGNTaykWIa0ojdBSqyZ7j8IM="),
"keyId" : NumberLong("6529469760260800513")
}
}
}
上面是將節(jié)點(diǎn)27019變?yōu)檠舆t節(jié)點(diǎn),延遲時(shí)間為4分鐘,現(xiàn)在往主節(jié)點(diǎn)數(shù)據(jù)庫(kù)mydb的testHello集合插入一條數(shù)據(jù)
myrepl:PRIMARY> use mydb
switched to db mydb
myrepl:PRIMARY> db.testHello.insert({name:"hello",pwd:"world"})
WriteResult({ "nInserted" : 1 })
插入之后,在4分鐘之內(nèi)進(jìn)入延遲節(jié)點(diǎn),發(fā)現(xiàn)數(shù)據(jù)并沒(méi)有同步,說(shuō)明配置延遲節(jié)點(diǎn)成功
myrepl:SECONDARY> conn = new Mongo("127.0.0.1:27019")
connection to 127.0.0.1:27019
myrepl:SECONDARY> conn.setSlaveOk()
myrepl:SECONDARY> db = conn.getDB("mydb")
mydb
myrepl:SECONDARY> db.auth("test","test")
1
myrepl:SECONDARY> db.testHello.find()
myrepl:SECONDARY>
4分鐘之后,再進(jìn)入延遲節(jié)點(diǎn)查看,發(fā)現(xiàn)數(shù)據(jù)同步了
myrepl:SECONDARY> db.testHello.find()
{ "_id" : ObjectId("5a9e93f9bff707d8f99916fe"), "name" : "hello", "pwd" : "world" }
仲裁節(jié)點(diǎn)
現(xiàn)在副本集的組成為1個(gè)主節(jié)點(diǎn)+1個(gè)延遲節(jié)點(diǎn)+1個(gè)隱藏節(jié)點(diǎn)。現(xiàn)在將隱藏節(jié)點(diǎn)刪除,再以添加仲裁節(jié)點(diǎn)的方式將此節(jié)點(diǎn)重新添加回來(lái)
myrepl:PRIMARY> rs.remove("127.0.0.1:27018")
{
"ok" : 1,
/............
}
myrepl:PRIMARY> rs.addArb("127.0.0.1:27018")
{
"ok" : 1,
//..............
}
}
myrepl:PRIMARY> rs.config()
{
"_id" : "myrepl",
"version" : 14,
"protocolVersion" : NumberLong(1),
"members" : [
{
"_id" : 0,
"host" : "127.0.0.1:27017",
//......
},
{
"_id" : 2,
"host" : "127.0.0.1:27019",
//......
},
{
"_id" : 3,
"host" : "127.0.0.1:27018",
"arbiterOnly" : true,
"buildIndexes" : true,
"hidden" : false,
"priority" : 0,
"tags" : {
},
"slaveDelay" : NumberLong(0),
"votes" : 1
}
],
//.................
}