一组Mongodb复制集,就是一组mongod进程,这些进程维护同一个数据集合。复制集提供了数据冗余和高等级的可靠性,是生产部署的基础。
目的
保证数据在生产部署时的冗余和可靠性,通过在不同的机器上保存副本来保证数据的不会因为单点损坏而丢失。能够随时应对数据丢失、机器损坏带来的风险。提高读取能力,用户的读取服务器和写入服务器在不同的地方,而且,由不同的服务器为不同的用户提供服务,提高整个系统的负载。机制
一组复制集就是一组mongod实例掌管同一个数据集,实例可以在不同的机器上面。实例中包含一个主导(Primary),接受客户端所有的写入操作,其他都是副本实例(Secondary),从主服务器上获得数据并保持同步。主服务器很重要,包含了所有的改变操作(写)的日志。但是副本服务器集群包含有所有的主服务器数据,因此当主服务器挂掉了,就会在副本服务器上重新选取一个成为主服务器。每个复制集还有一个仲裁者(Arbiter),仲裁者不存储数据,只是负责通过心跳包来确认集群中集合的数量, 并在主服务器选举的时候作为仲裁决定结果。三个存储数据的复制 假设一个复制集由一个主,两个从库组成,主库宕机时,这两个从库都可以被选为主库。 当主库宕机后,两个从库都会进行竞选,其中一个变为主库,当原主库恢复后,作为从库加入当前的复制集群即可。
存在arbiter节点的复制集 一个主库,一个从库,可以在选举中成为主库,一个arbiter节点,在选举中,只进行投票,不能成为主库。 说明:由于arbiter节点没有复制数据,因此这个架构中仅提供一个完整的数据副本。arbiter节点只需要更少的资源,代价是更有限的冗余和容错。 当主库宕机时,将会选择从库成为主,主库修复后,将其加入到现有的复制集群中即可。
复制集通过replSetInitiate命令(或mongo shell的rs.initiate())进行初始化,初始化后各个成员间开始发送心跳消息,并发起Priamry选举操作,获得『大多数』成员投票支持的节点,会成为Primary,其余节点成为Secondary。 『大多数』的定义 假设复制集内投票成员数量为N,则大多数为 N/2 + 1,当复制集内存活成员数量不足大多数时,整个复制集将无法选举出Primary,复制集将无法提供写服务,处于只读状态。
创建3个mongo容器
docker create --name mongo01 -p 27017:27017 -v mongo-data-01:/data/db mongo -replSet "rs0" --bind_ip_all docker create --name mongo02 -p 27018:27017 -v mongo-data-02:/data/db mongo -replSet "rs0" --bind_ip_all docker create --name mongo03 -p 27019:27017 -v mongo-data-03:/data/db mongo -replSet "rs0" --bind_ip_all启动容器
docker start mongo01 mongo02 mongo03进入容器操作
docker exec -it mongo01 /bin/bash登录到mongo服务
mongo 192.168.130.128:27017初始化复制集集群
rs.initiate({ _id : "rs0", members: [ { _id: 0, host: "192.168.130.128:27017" }, { _id: 1, host: "192.168.130.128:27018" }, { _id: 2, host: "192.168.130.128:27019" } ] })响应
{ "ok" : 1, #成功 "$clusterTime" : { "clusterTime" : Timestamp(1603336388, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } }, "operationTime" : Timestamp(1603336388, 1) }查看集群状态
rs.status()响应
{ "set" : "rs0", "date" : ISODate("2020-10-22T03:14:38.732Z"), "myState" : 1, "term" : NumberLong(1), "syncSourceHost" : "", "syncSourceId" : -1, "heartbeatIntervalMillis" : NumberLong(2000), "majorityVoteCount" : 2, "writeMajorityCount" : 2, "votingMembersCount" : 3, "writableVotingMembersCount" : 3, "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1603336469, 1), "t" : NumberLong(1) }, "lastCommittedWallTime" : ISODate("2020-10-22T03:14:29.165Z"), "readConcernMajorityOpTime" : { "ts" : Timestamp(1603336469, 1), "t" : NumberLong(1) }, "readConcernMajorityWallTime" : ISODate("2020-10-22T03:14:29.165Z"), "appliedOpTime" : { "ts" : Timestamp(1603336469, 1), "t" : NumberLong(1) }, "durableOpTime" : { "ts" : Timestamp(1603336469, 1), "t" : NumberLong(1) }, "lastAppliedWallTime" : ISODate("2020-10-22T03:14:29.165Z"), "lastDurableWallTime" : ISODate("2020-10-22T03:14:29.165Z") }, "lastStableRecoveryTimestamp" : Timestamp(1603336459, 1), "electionCandidateMetrics" : { "lastElectionReason" : "electionTimeout", "lastElectionDate" : ISODate("2020-10-22T03:13:18.828Z"), "electionTerm" : NumberLong(1), "lastCommittedOpTimeAtElection" : { "ts" : Timestamp(0, 0), "t" : NumberLong(-1) }, "lastSeenOpTimeAtElection" : { "ts" : Timestamp(1603336388, 1), "t" : NumberLong(-1) }, "numVotesNeeded" : 2, "priorityAtElection" : 1, "electionTimeoutMillis" : NumberLong(10000), "numCatchUpOps" : NumberLong(0), "newTermStartDate" : ISODate("2020-10-22T03:13:19.030Z"), "wMajorityWriteAvailabilityDate" : ISODate("2020-10-22T03:13:20.293Z") }, "members" : [ { "_id" : 0, "name" : "192.168.130.128:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", #主节点 "uptime" : 280, "optime" : { "ts" : Timestamp(1603336469, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2020-10-22T03:14:29Z"), "syncSourceHost" : "", "syncSourceId" : -1, "infoMessage" : "", "electionTime" : Timestamp(1603336398, 1), "electionDate" : ISODate("2020-10-22T03:13:18Z"), "configVersion" : 1, "configTerm" : 1, "self" : true, "lastHeartbeatMessage" : "" }, { "_id" : 1, "name" : "192.168.130.128:27018", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", #从节点 "uptime" : 90, "optime" : { "ts" : Timestamp(1603336469, 1), "t" : NumberLong(1) }, "optimeDurable" : { "ts" : Timestamp(1603336469, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2020-10-22T03:14:29Z"), "optimeDurableDate" : ISODate("2020-10-22T03:14:29Z"), "lastHeartbeat" : ISODate("2020-10-22T03:14:37.082Z"), "lastHeartbeatRecv" : ISODate("2020-10-22T03:14:38.594Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "", "syncSourceHost" : "192.168.130.128:27017", "syncSourceId" : 0, "infoMessage" : "", "configVersion" : 1, "configTerm" : 1 }, { "_id" : 2, "name" : "192.168.130.128:27019", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", #从节点 "uptime" : 90, "optime" : { "ts" : Timestamp(1603336469, 1), "t" : NumberLong(1) }, "optimeDurable" : { "ts" : Timestamp(1603336469, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2020-10-22T03:14:29Z"), "optimeDurableDate" : ISODate("2020-10-22T03:14:29Z"), "lastHeartbeat" : ISODate("2020-10-22T03:14:37.087Z"), "lastHeartbeatRecv" : ISODate("2020-10-22T03:14:38.605Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "", "syncSourceHost" : "192.168.130.128:27017", "syncSourceId" : 0, "infoMessage" : "", "configVersion" : 1, "configTerm" : 1 } ], "ok" : 1, "$clusterTime" : { "clusterTime" : Timestamp(1603336469, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } }, "operationTime" : Timestamp(1603336469, 1) }主库插入数据
rs0:PRIMARY> use test switched to db test rs0:PRIMARY> db.user.insert({"id":1001,"name":"zhangsan"}) WriteResult({ "nInserted" : 1 })切换到复制库
rs0:PRIMARY> exit bye root@388f80cc2cfd:/# mongo 192.168.130.128:27018在复制库查询数据 默认情况下从库是不允许读写操作的
rs0:SECONDARY> use test switched to db test rs0:SECONDARY> db.user.find() #出错,默认情况下从库是不允许读写操作的 Error: error: { "topologyVersion" : { "processId" : ObjectId("5f90f806ee00d26cdccc1d22"), "counter" : NumberLong(4) }, "operationTime" : Timestamp(1603336789, 1), "ok" : 0, "errmsg" : "not master and slaveOk=false", "code" : 13435, "codeName" : "NotMasterNoSlaveOk", "$clusterTime" : { "clusterTime" : Timestamp(1603336789, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }设置允许从库读取数据
rs0:SECONDARY> rs.secondaryOk() rs0:SECONDARY> db.user.find() { "_id" : ObjectId("5f90f9c2d5a6806dd78d4879"), "id" : 1001, "name" : "zhangsan" }