副本集就是有自动故障恢复功能的主从集群。主从集群和副本集最为明显的区别就是副本集没有固定的主节点:整个集群会选举出一个主节点,当其不能工作时,则变更到其它节点。副本集总会有一个活跃节点和一个或多个备份节点。
副本集最好的优点就是全自动化的。
standard:常规节点,存储一份完整的数据副本,参与选举投票,可能称为活跃节点。
passive:存储完整的数据副本,参与投票,不能成为活跃节点。
arbiter:仲裁者只负责投票,不接受复制数据,也不能成为活跃节点。
Replica Sets复制(副本集)
在每台MongoDB上执行(三台机器,一主一从一仲裁):
/opt/master/bin/mongod --dbpath /data/master/ --logpath /opt/master/log/mongodb.log --fork --replSet rs1 /opt/slave1/bin/mongod --dbpath /data/slave1/ --logpath /opt/slave1/log/mongodb.log --fork --replSet rs1 /opt/slave2/bin/mongod --dbpath /data/slave2/ --logpath /opt/slave2/log/mongodb.log --fork --replSet rs1日志会输出:
startup; NoMatchingDocument: Did not find replica set configuration document in local.system.replse初始化副本(任意一台机器执行):
# /opt/master/bin/mongo #进入MongoDB Shell程序,类似SQL命令行 > use admin; #使用admin数据库 switched to db admin#定义副本集配置变量,这里的 _id:”repset” 和上面命令参数“ –replSet repset” 要保持一样。 配置:
> config={_id:"rs1",members:[{_id:0,host:"192.168.0.108:27017",priority:2},{_id:1,host:"192.168.0.109:27017",priority:1},{_id:2,host:"192.168.0.113:27017",priority:0,arbiterOnly:true}]} { "_id" : "rs1", "members" : [ { "_id" : 0, "host" : "192.168.0.108:27017", "priority" : 2 }, { "_id" : 1, "host" : "192.168.0.109:27017", "priority" : 1 }, { "_id" : 2, "host" : "192.168.0.113:27017", "priority" : 0, "arbiterOnly" : true } ] } > rs.initiate(config); { "ok" : 1 }最后应该注意一点,要想使用副本集,从的mongodb的数据必须都是空的,要不然执行 rs.initiate()命令时会提示因存在数据而导致初始化不成功,可以使用 关闭所有副本集,在从节点上执行如下命令,执行完成后启动群集
/opt/slave/bin/mongod --dbpath /data/slave/ --logpath /opt/slave/log/mongodb.log --fork --rest > show dbs; admin 0.000GB local 0.000GB > use admin; switched to db admin > db.dropDatabase(); { "dropped" : "admin", "ok" : 1 } > use local; switched to db local > db.dropDatabase(); { "dropped" : "local", "ok" : 1 } > exit验证:
rs1:OTHER> rs.status(); { "set" : "rs1", "date" : ISODate("2017-04-09T17:31:27.040Z"), "myState" : 1, "term" : NumberLong(1), "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1491759084, 2), "t" : NumberLong(1) }, "appliedOpTime" : { "ts" : Timestamp(1491759084, 2), "t" : NumberLong(1) }, "durableOpTime" : { "ts" : Timestamp(1491759084, 2), "t" : NumberLong(1) } }, "members" : [ { "_id" : 0, "name" : "192.168.0.108:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 300, "optime" : { "ts" : Timestamp(1491759084, 2), "t" : NumberLong(1) }, "optimeDate" : ISODate("2017-04-09T17:31:24Z"), "infoMessage" : "could not find member to sync from", "electionTime" : Timestamp(1491759084, 1), "electionDate" : ISODate("2017-04-09T17:31:24Z"), "configVersion" : 1, "self" : true }, { "_id" : 1, "name" : "192.168.0.109:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 13, "optime" : { "ts" : Timestamp(1491759084, 2), "t" : NumberLong(1) }, "optimeDurable" : { "ts" : Timestamp(1491759084, 2), "t" : NumberLong(1) }, "optimeDate" : ISODate("2017-04-09T17:31:24Z"), "optimeDurableDate" : ISODate("2017-04-09T17:31:24Z"), "lastHeartbeat" : ISODate("2017-04-09T17:31:26.336Z"), "lastHeartbeatRecv" : ISODate("2017-04-09T17:31:25.667Z"), "pingMs" : NumberLong(0), "syncingTo" : "192.168.0.108:27017", "configVersion" : 1 }, { "_id" : 2, "name" : "192.168.0.113:27017", "health" : 1, "state" : 7, "stateStr" : "ARBITER", "uptime" : 13, "lastHeartbeat" : ISODate("2017-04-09T17:31:26.336Z"), "lastHeartbeatRecv" : ISODate("2017-04-09T17:31:25.489Z"), "pingMs" : NumberLong(1), "configVersion" : 1 } ], "ok" : 1 } rs1:PRIMARY>"stateStr" : "PRIMARY" #主节点 "stateStr" : "SECONDARY" #从节点 "stateStr" : "ARBITER" #仲裁节点 #查看集群节点的状态 rs.status(); rs.config(); 日志信息:
2017-04-10T01:31:18.737+0800 I REPL [ReplicationExecutor] Member 192.168.0.109:27017 is now in state SECONDARY 2017-04-10T01:31:18.737+0800 I REPL [ReplicationExecutor] Member 192.168.0.113:27017 is now in state ARBITER在主节点插入数据测试:
rs1:PRIMARY> use wgl switched to db wgl rs1:PRIMARY> db.wgl.insert({"1":"1"}) WriteResult({ "nInserted" : 1 }) rs1:PRIMARY> db.wgl.find() { "_id" : ObjectId("58eb5bce63e260f6cd98a84d"), "1" : "1" }测试从节点可读不:
rs1:SECONDARY> use wgl; switched to db wgl rs1:SECONDARY> db.wgl.find(); { "_id" : ObjectId("58eb5bce63e260f6cd98a84d"), "1" : "1" } rs1:SECONDARY> db.wgl.insert({"2":"2"}) WriteResult({ "writeError" : { "code" : 10107, "errmsg" : "not master" } }) rs1:SECONDARY> db.wgl.find(); { "_id" : ObjectId("58eb5bce63e260f6cd98a84d"), "1" : "1" } rs1:SECONDARY> show tables wgl rs1:SECONDARY> show tabels; 2017-04-10T01:41:30.048+0800 E QUERY [thread1] Error: don't know how to show [tabels] : shellHelper.show@src/mongo/shell/utils.js:898:11 shellHelper@src/mongo/shell/utils.js:651:15 @(shellhelp2):1:1 rs1:SECONDARY> db.getMongo().setSlaveOk(); rs1:SECONDARY> db.test.find(); { "_id" : ObjectId("58ea71fd2836cb8795c0d42c"), "1" : "1" }关闭主节点
/opt/master/bin/mongod --dbpath /data/master/ --shutdown #主节点执行 REPL [ReplicationExecutor] transition to PRIMARY #从节点日志反馈 [rsSync] transition to primary complete; database writes are now permitted #从日志写入开始#从节点登陆
/opt/master/bin/mongo rs1:PRIMARY> #变成主了
rs1:PRIMARY> rs.status(); { "set" : "rs1", "date" : ISODate("2017-04-09T17:50:59.116Z"), "myState" : 1, "term" : NumberLong(2), "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1491759965, 1), "t" : NumberLong(1) }, "appliedOpTime" : { "ts" : Timestamp(1491760251, 1), "t" : NumberLong(2) }, "durableOpTime" : { "ts" : Timestamp(1491760251, 1), "t" : NumberLong(2) } }, "members" : [ { "_id" : 0, "name" : "192.168.0.108:27017", "health" : 0, "state" : 8, "stateStr" : "(not reachable/healthy)", "uptime" : 0, "optime" : { "ts" : Timestamp(0, 0), "t" : NumberLong(-1) }, "optimeDurable" : { "ts" : Timestamp(0, 0), "t" : NumberLong(-1) }, "optimeDate" : ISODate("1970-01-01T00:00:00Z"), "optimeDurableDate" : ISODate("1970-01-01T00:00:00Z"), "lastHeartbeat" : ISODate("2017-04-09T17:50:57.640Z"), "lastHeartbeatRecv" : ISODate("2017-04-09T17:46:11.614Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "Connection refused", "configVersion" : -1 }, { "_id" : 1, "name" : "192.168.0.109:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 1465, "optime" : { "ts" : Timestamp(1491760251, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2017-04-09T17:50:51Z"), "electionTime" : Timestamp(1491759981, 1), "electionDate" : ISODate("2017-04-09T17:46:21Z"), "configVersion" : 1, "self" : true }, { "_id" : 2, "name" : "192.168.0.113:27017", "health" : 1, "state" : 7, "stateStr" : "ARBITER", "uptime" : 1183, "lastHeartbeat" : ISODate("2017-04-09T17:50:57.369Z"), "lastHeartbeatRecv" : ISODate("2017-04-09T17:50:56.223Z"), "pingMs" : NumberLong(0), "configVersion" : 1 } ], "ok" : 1 }三台机器主从状态分别如下: "stateStr" : "(not reachable/healthy)", "stateStr" : "PRIMARY", "stateStr" : "ARBITER", 查看群集IP信息:
rs1:PRIMARY> rs.isMaster(); { "hosts" : [ "192.168.1.119:27017", "192.168.1.151:27017" ], "arbiters" : [ "192.168.1.251:27017" ], "setName" : "rs1", "setVersion" : 1, "ismaster" : true, "secondary" : false, "primary" : "192.168.1.119:27017", "me" : "192.168.1.119:27017", "electionId" : ObjectId("7fffffff0000000000000006"), "lastWrite" : { "opTime" : { "ts" : Timestamp(1491820361, 1), "t" : NumberLong(6) }, "lastWriteDate" : ISODate("2017-04-10T10:32:41Z") }, "maxBsonObjectSize" : 16777216, "maxMessageSizeBytes" : 48000000, "maxWriteBatchSize" : 1000, "localTime" : ISODate("2017-04-10T10:32:44.800Z"), "maxWireVersion" : 5, "minWireVersion" : 0, "readOnly" : false, "ok" : 1 }恢复原先主节点: 查看主节点的日志发现
I REPL [ReplicationExecutor] Member 192.168.1.251:27017 is now in state ARBITER I REPL [ReplicationExecutor] Member 192.168.1.151:27017 is now in state PRIMARY发现原先主节点抢回了主节点地位,拥有占先权! 通过rs.add(‘ip:port’)增加节点
rs1:PRIMARY> rs.add('192.168.1.151:27018') { "ok" : 1 } rs1:PRIMARY> rs.status(); { "set" : "rs1", "date" : ISODate("2017-04-10T10:35:01.220Z"), "myState" : 1, "term" : NumberLong(6), "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1491820498, 1), "t" : NumberLong(6) }, "appliedOpTime" : { "ts" : Timestamp(1491820498, 1), "t" : NumberLong(6) }, "durableOpTime" : { "ts" : Timestamp(1491820498, 1), "t" : NumberLong(6) } }, "members" : [ { "_id" : 0, "name" : "192.168.1.119:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 641, "optime" : { "ts" : Timestamp(1491820498, 1), "t" : NumberLong(6) }, "optimeDate" : ISODate("2017-04-10T10:34:58Z"), "electionTime" : Timestamp(1491819871, 2), "electionDate" : ISODate("2017-04-10T10:24:31Z"), "configVersion" : 2, "self" : true }, { "_id" : 1, "name" : "192.168.1.151:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 640, "optime" : { "ts" : Timestamp(1491820498, 1), "t" : NumberLong(6) }, "optimeDurable" : { "ts" : Timestamp(1491820498, 1), "t" : NumberLong(6) }, "optimeDate" : ISODate("2017-04-10T10:34:58Z"), "optimeDurableDate" : ISODate("2017-04-10T10:34:58Z"), "lastHeartbeat" : ISODate("2017-04-10T10:35:00.594Z"), "lastHeartbeatRecv" : ISODate("2017-04-10T10:34:58.645Z"), "pingMs" : NumberLong(4), "configVersion" : 2 }, { "_id" : 2, "name" : "192.168.1.251:27017", "health" : 1, "state" : 7, "stateStr" : "ARBITER", "uptime" : 640, "lastHeartbeat" : ISODate("2017-04-10T10:35:00.564Z"), "lastHeartbeatRecv" : ISODate("2017-04-10T10:34:58.573Z"), "pingMs" : NumberLong(0), "configVersion" : 2 }, { "_id" : 3, "name" : "192.168.1.151:27018", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 2, "optime" : { "ts" : Timestamp(1491820498, 1), "t" : NumberLong(6) }, "optimeDurable" : { "ts" : Timestamp(1491820498, 1), "t" : NumberLong(6) }, "optimeDate" : ISODate("2017-04-10T10:34:58Z"), "optimeDurableDate" : ISODate("2017-04-10T10:34:58Z"), "lastHeartbeat" : ISODate("2017-04-10T10:35:00.564Z"), "lastHeartbeatRecv" : ISODate("2017-04-10T10:34:59.308Z"), "pingMs" : NumberLong(1), "configVersion" : 2 } ], "ok" : 1 }移除节点用 rs.remove(“ip:port”); 再次查看集群状态发现节点被移除了
rs1:PRIMARY> rs.remove('192.168.1.151:27018') { "ok" : 1 }停掉主节点:
192.168.1.151:27018变成主了 rs1:SECONDARY> rs.status(); { "set" : "rs1", "date" : ISODate("2017-04-10T10:38:44.358Z"), "myState" : 2, "term" : NumberLong(7), "syncingTo" : "192.168.1.151:27018", "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1491820691, 1), "t" : NumberLong(6) }, "appliedOpTime" : { "ts" : Timestamp(1491820722, 1), "t" : NumberLong(7) }, "durableOpTime" : { "ts" : Timestamp(1491820722, 1), "t" : NumberLong(7) } }, "members" : [ { "_id" : 0, "name" : "192.168.1.119:27017", "health" : 0, "state" : 8, "stateStr" : "(not reachable/healthy)", "uptime" : 0, "optime" : { "ts" : Timestamp(0, 0), "t" : NumberLong(-1) }, "optimeDurable" : { "ts" : Timestamp(0, 0), "t" : NumberLong(-1) }, "optimeDate" : ISODate("1970-01-01T00:00:00Z"), "optimeDurableDate" : ISODate("1970-01-01T00:00:00Z"), "lastHeartbeat" : ISODate("2017-04-10T10:38:44.238Z"), "lastHeartbeatRecv" : ISODate("2017-04-10T10:38:11.731Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "Connection refused", "configVersion" : -1 }, { "_id" : 1, "name" : "192.168.1.151:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 1388, "optime" : { "ts" : Timestamp(1491820722, 1), "t" : NumberLong(7) }, "optimeDate" : ISODate("2017-04-10T10:38:42Z"), "syncingTo" : "192.168.1.151:27018", "configVersion" : 4, "self" : true }, { "_id" : 2, "name" : "192.168.1.251:27017", "health" : 1, "state" : 7, "stateStr" : "ARBITER", "uptime" : 1304, "lastHeartbeat" : ISODate("2017-04-10T10:38:44.214Z"), "lastHeartbeatRecv" : ISODate("2017-04-10T10:38:43.288Z"), "pingMs" : NumberLong(1), "configVersion" : 4 }, { "_id" : 3, "name" : "192.168.1.151:27018", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 96, "optime" : { "ts" : Timestamp(1491820722, 1), "t" : NumberLong(7) }, "optimeDurable" : { "ts" : Timestamp(1491820722, 1), "t" : NumberLong(7) }, "optimeDate" : ISODate("2017-04-10T10:38:42Z"), "optimeDurableDate" : ISODate("2017-04-10T10:38:42Z"), "lastHeartbeat" : ISODate("2017-04-10T10:38:44.213Z"), "lastHeartbeatRecv" : ISODate("2017-04-10T10:38:43.986Z"), "pingMs" : NumberLong(0), "electionTime" : Timestamp(1491820701, 1), "electionDate" : ISODate("2017-04-10T10:38:21Z"), "configVersion" : 4 } ], "ok" : 1 }