副本集就是有自动故障恢复功能的主从集群。主从集群和副本集最为明显的区别就是副本集没有固定的主节点:整个集群会选举出一个主节点,当其不能工作时,则变更到其它节点。副本集总会有一个活跃节点和一个或多个备份节点。
副本集最好的优点就是全自动化的。
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.000GBlocal 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 SECONDARY2017-04-10T01:31:18.737+0800 I REPL [ReplicationExecutor] Member 192.168.0.113:27017 is now in state ARBITER

在主节点插入数据测试:

rs1:PRIMARY> use wglswitched to db wglrs1: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 wglrs1: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 tableswglrs1: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:11shellHelper@src/mongo/shell/utils.js:651:15@(shellhelp2):1:1rs1: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/mongors1: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 ARBITERI 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}