前文我們聊到了mongodb的索引的相關作用和介紹以及索引的管理,回顧請參考https://www.cnblogs.com/qiuhom-1874/p/13950287.html;今天我們來聊下mongodb的副本集;
1、什么是副本集?主要作用是什么?它是怎么工作的呢?
所謂副本集是指服務於同一數據集的多個mongodb實例;這意味着在mongodb中數據集副本的數量可以是多個,每個副本的數據都是一樣的;副本的存在主要作用是對mongodb數據庫中的數據做冗余備份和提高數據服務的可用性;在mongodb中對數據做冗余的方式有兩種,一種是主從架構(master/slave),這種架構和mysql中的主從架構沒有什么不同,但是在mongodb中,主從架構的方式幾乎沒有人用,處於廢棄的狀態。。另外一種是副本集(replica set),副本集本質上也是主從架構的一種,它和我們剛才說的主從架構,有一個顯著的區別,副本集支持故障自動轉移,不需要人工手動干預;所謂的故障自動轉移是指當一個副本集中主節點因各種原因宕機或者從節點在一定時間內沒有收到主節點發送到心跳信息,此時就會觸發從節點重新選舉主節點的操作;在多個從節點中選舉出來一個主節點以后,其他從節點能夠自動指向新選舉的主節點同步數據,從而完成這次故障轉移;
2、mongodb副本集架構
提示:對於mongodb中的副本集來說,為了保證數據的一致性,一個副本集中只能有一個主節點,並且只有主節點才擁有讀寫權限,其他從節點都只有讀權限;在mongodb的副本集中,主節點我們叫做primary,從節點叫做secondary;
3、副本集是通過什么來復制數據的呢?
我知道mysql的主從復制是通過主節點記錄寫操作到binlog中,從節點通過同步主節點的binlog,然后把同步的binlog在本地進行重放,從而實現數據同步;在mongodb中也是類似的操作,不同的是在mongodb中主節點將寫操作記錄到oplog(操作日志),各從節點通過復制主節點的oplog,在本地重放實現數據同步;oplog的作用和mysql中的binlog的作用很類似,都是用來記錄寫操作事件,都是用來同步數據;mongodb中的oplog是一個定容集合,所謂定容是指它的大小是固定的,它不會像binlog隨日志的增多而逐漸變大;通常oplog我們不人為指定其大小,默認就是占用所在文件系統空閑磁盤的5%;除了大小是不變的,它還有一個冪等的特點;所謂冪等就是不管在monogdb中執行多少次oplog中的操作,其最終的結果都是一樣的;如果寫操作日志把oplog寫滿了,它會從頭覆蓋之前的oplog,依次循環寫oplog;
4、副本集是怎么完成故障轉移的?
在前邊我們聊zookeeper時,聊到過分布式場景中的選舉過程;mongodb的主節點故障以后,也是通過這種機制來完成選舉;在mongodb的副本集中,主節點每隔兩秒向各從節點發送心跳信息,各從節點通過檢測主節點的心跳信息來判定主節點是否存活;如果在一定的時間范圍內,從節點沒有收到主節點發送到心跳信息,此時從節點會認為主節點掛了,從而觸發主節點選舉的操作;這個選舉和zookeeper里的選舉很類似,通常情況都是大於集群總票的一半一方可以代表集群正常工作;所以通常情況mongodb副本集都是基數個節點組成;當然mongodb中也可以是偶數節點(正常提供服務的節點),如果是偶數節點,通常會借助一個仲裁節點來完成選舉;仲裁節點擁有選票,但不被選舉成為主節點,也不擁有副本數據,同時它必須要能夠檢測主節點心跳;簡單總結就是mongodb副本集是通過從節點檢測主節點心跳來判斷主節點是否存活,如果在一定時間范圍內,沒有檢測到主節點的心跳信息,此時就會觸發主節點選舉操作,如果集群節點數量為偶數個,通常會借助仲裁節點來完成選舉;從而實現完成故障自動轉移;
5、mongodb副本集中特殊類型節點分類
0優先級節點:這種節點的特點是優先級為0,可參與選舉,擁有副本數據,但不被選舉成為主節點,可讀不可寫;這種節點我們也叫冷備節點;通常用於異地容災使用;
被隱藏的從節點:這種節點的特點是,可參與選舉,擁有副本數據,但不被選舉成為主節點,對客戶端不可讀寫也不可見;通常用於同步同一副本集中的其他節點的不同工作流的場景中;
延遲復制的從節點:這種節點的特點是,副本數據落后主節點一個時間窗口,也就說這種節點上的副本數據總是過期的;除此它可參與選舉,不可被選舉為主節點;主要用於特殊功用;比如在主節點上執行了一個刪除操作,我們可以在延遲復制的從節點上把數據找回;
arbiter節點:這種就是我們說的仲裁節點,它可參與選舉,不擁有副本數據,不被選舉成為主節點,不可讀寫;主要作用是輔助判定主節點是否存活,輔助從節點完成選舉,實現故障轉移;
6、創建mongodb的副本集合
環境准備
主機名 | ip地址 |
node01 | 192.168.0.41 |
node02 | 192.168.0.42 |
node03 | 192.168.0.43 |
在三個節點上分別做好時間同步,關閉selinux,關閉防火牆,主機名解析,有必要還可以做ssh互信。准備好基礎環境以后,在三台server上配置mongodb的yum源
[root@node01 ~]# cat /etc/yum.repos.d/mongodb.repo [mongodb-org] name = MongoDB Repository baseurl = https://mirrors.aliyun.com/mongodb/yum/redhat/7/mongodb-org/4.4/x86_64/ gpgcheck = 1 enabled = 1 gpgkey = https://www.mongodb.org/static/pgp/server-4.4.asc [root@node01 ~]# scp /etc/yum.repos.d/mongodb.repo node02:/etc/yum.repos.d/ mongodb.repo 100% 206 80.4KB/s 00:00 [root@node01 ~]# scp /etc/yum.repos.d/mongodb.repo node03:/etc/yum.repos.d/ mongodb.repo 100% 206 88.9KB/s 00:00 [root@node01 ~]#
安裝mongodb-org
yum install -y mongodb-org
准備數據目錄和存放日志的目錄,並將其屬主和屬組更改為mongod用戶
[root@node01 ~]# mkdir -pv /mongodb/{data,log} mkdir: created directory ‘/mongodb’ mkdir: created directory ‘/mongodb/data’ mkdir: created directory ‘/mongodb/log’ [root@node01 ~]# chown -R mongod.mongod /mongodb/ [root@node01 ~]# ll -d /mongodb/ drwxr-xr-x 4 mongod mongod 29 Nov 10 19:36 /mongodb/ [root@node01 ~]#
提示:以上需在三個節點都要做一遍;
配置mongodb
提示:主要配置replication,其中oplogSizeMB用來指定oplog的大小,默認不指定,其大小就是所在文件系統空閑磁盤的%5;replSetName用來指定副本集的名稱,這個名稱非常重要,主要用來標識不同副本集;enableMajorityReadConcern用來指定是否開啟mongodb周期性的做快照,並記錄oplog的時間戳;
完整配置

[root@node01 ~]# cat /etc/mongod.conf systemLog: destination: file logAppend: true path: /mongodb/log/mongod.log storage: dbPath: /mongodb/data/ journal: enabled: true processManagement: fork: true pidFilePath: /var/run/mongodb/mongod.pid timeZoneInfo: /usr/share/zoneinfo net: port: 27017 bindIp: 0.0.0.0 replication: oplogSizeMB: 2048 replSetName: test_replset enableMajorityReadConcern: false #security: #operationProfiling: #sharding: ## Enterprise-Only Options #auditLog: #snmp: [root@node01 ~]#
復制配置文件到node02,node03
[root@node01 ~]# scp /etc/mongod.conf node02:/etc/ mongod.conf 100% 494 315.1KB/s 00:00 [root@node01 ~]# scp /etc/mongod.conf node03:/etc/ mongod.conf 100% 494 351.6KB/s 00:00 [root@node01 ~]#
啟動服務
提示:請確保對應節點上的mongodb所監聽的端口正常即可;
連接三個節點任意一個節點查看副本集狀態
> rs.help() rs.status() { replSetGetStatus : 1 } checks repl set status rs.initiate() { replSetInitiate : null } initiates set with default settings rs.initiate(cfg) { replSetInitiate : cfg } initiates set with configuration cfg rs.conf() get the current configuration object from local.system.replset rs.reconfig(cfg) updates the configuration of a running replica set with cfg (disconnects) rs.add(hostportstr) add a new member to the set with default attributes (disconnects) rs.add(membercfgobj) add a new member to the set with extra attributes (disconnects) rs.addArb(hostportstr) add a new member which is arbiterOnly:true (disconnects) rs.stepDown([stepdownSecs, catchUpSecs]) step down as primary (disconnects) rs.syncFrom(hostportstr) make a secondary sync from the given member rs.freeze(secs) make a node ineligible to become primary for the time specified rs.remove(hostportstr) remove a host from the replica set (disconnects) rs.secondaryOk() allow queries on secondary nodes rs.printReplicationInfo() check oplog size and time range rs.printSecondaryReplicationInfo() check replica set members and replication lag db.isMaster() check who is primary reconfiguration helpers disconnect from the database so the shell will display an error, even if the command succeeds. > rs.status() { "operationTime" : Timestamp(0, 0), "ok" : 0, "errmsg" : "no replset config has been received", "code" : 94, "codeName" : "NotYetInitialized", "$clusterTime" : { "clusterTime" : Timestamp(0, 0), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } } >
提示:所有關於副本集的操作命令都可以使用rs.help()方法去查看幫助;其中rs.status()是用來查看副本集狀態;這里顯示沒有副本集配置,其原因是沒有初始化副本集;
初始化副本集
> rs.initiate() { "info2" : "no configuration specified. Using a default configuration for the set", "me" : "node01.test.org:27017", "ok" : 1, "$clusterTime" : { "clusterTime" : Timestamp(1605010821, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } }, "operationTime" : Timestamp(1605010821, 1) } test_replset:SECONDARY> rs.status() { "set" : "test_replset", "date" : ISODate("2020-11-10T12:20:32.079Z"), "myState" : 1, "term" : NumberLong(1), "syncSourceHost" : "", "syncSourceId" : -1, "heartbeatIntervalMillis" : NumberLong(2000), "majorityVoteCount" : 1, "writeMajorityCount" : 1, "votingMembersCount" : 1, "writableVotingMembersCount" : 1, "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1605010821, 8), "t" : NumberLong(1) }, "lastCommittedWallTime" : ISODate("2020-11-10T12:20:21.720Z"), "readConcernMajorityOpTime" : { "ts" : Timestamp(1605010821, 8), "t" : NumberLong(1) }, "readConcernMajorityWallTime" : ISODate("2020-11-10T12:20:21.720Z"), "appliedOpTime" : { "ts" : Timestamp(1605010821, 8), "t" : NumberLong(1) }, "durableOpTime" : { "ts" : Timestamp(1605010821, 8), "t" : NumberLong(1) }, "lastAppliedWallTime" : ISODate("2020-11-10T12:20:21.720Z"), "lastDurableWallTime" : ISODate("2020-11-10T12:20:21.720Z") }, "electionCandidateMetrics" : { "lastElectionReason" : "electionTimeout", "lastElectionDate" : ISODate("2020-11-10T12:20:21.632Z"), "electionTerm" : NumberLong(1), "lastCommittedOpTimeAtElection" : { "ts" : Timestamp(0, 0), "t" : NumberLong(-1) }, "lastSeenOpTimeAtElection" : { "ts" : Timestamp(1605010821, 1), "t" : NumberLong(-1) }, "numVotesNeeded" : 1, "priorityAtElection" : 1, "electionTimeoutMillis" : NumberLong(10000), "newTermStartDate" : ISODate("2020-11-10T12:20:21.694Z"), "wMajorityWriteAvailabilityDate" : ISODate("2020-11-10T12:20:21.719Z") }, "members" : [ { "_id" : 0, "name" : "node01.test.org:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 674, "optime" : { "ts" : Timestamp(1605010821, 8), "t" : NumberLong(1) }, "optimeDate" : ISODate("2020-11-10T12:20:21Z"), "syncSourceHost" : "", "syncSourceId" : -1, "infoMessage" : "Could not find member to sync from", "electionTime" : Timestamp(1605010821, 2), "electionDate" : ISODate("2020-11-10T12:20:21Z"), "configVersion" : 1, "configTerm" : 1, "self" : true, "lastHeartbeatMessage" : "" } ], "ok" : 1, "$clusterTime" : { "clusterTime" : Timestamp(1605010821, 8), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } }, "operationTime" : Timestamp(1605010821, 8) } test_replset:PRIMARY>
提示:初始化副本集以后,再次查看副本集狀態,它告訴我們有一個成員,其主機名為node01.test.org:27017,其health狀態為1,stateStr為PRIMARY等等一堆信息;
查看副本集配置
test_replset:PRIMARY> rs.conf() { "_id" : "test_replset", "version" : 1, "term" : 1, "protocolVersion" : NumberLong(1), "writeConcernMajorityJournalDefault" : true, "members" : [ { "_id" : 0, "host" : "node01.test.org:27017", "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("5faa85853d5a2d8fdf8af85e") } } test_replset:PRIMARY>
提示:可以看到心跳時間間隔為2s,超時為10s,選舉超時時長為10等等信息;
添加node02,node03節點到副本集成員
test_replset:PRIMARY> rs.add("node02") { "ok" : 1, "$clusterTime" : { "clusterTime" : Timestamp(1605011243, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } }, "operationTime" : Timestamp(1605011243, 1) } test_replset:PRIMARY> rs.add("node03") { "ok" : 1, "$clusterTime" : { "clusterTime" : Timestamp(1605011250, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } }, "operationTime" : Timestamp(1605011250, 1) } test_replset:PRIMARY> rs.status() { "set" : "test_replset", "date" : ISODate("2020-11-10T12:27:45.849Z"), "myState" : 1, "term" : NumberLong(1), "syncSourceHost" : "", "syncSourceId" : -1, "heartbeatIntervalMillis" : NumberLong(2000), "majorityVoteCount" : 2, "writeMajorityCount" : 2, "votingMembersCount" : 3, "writableVotingMembersCount" : 3, "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1605011261, 1), "t" : NumberLong(1) }, "lastCommittedWallTime" : ISODate("2020-11-10T12:27:41.873Z"), "readConcernMajorityOpTime" : { "ts" : Timestamp(1605011261, 1), "t" : NumberLong(1) }, "readConcernMajorityWallTime" : ISODate("2020-11-10T12:27:41.873Z"), "appliedOpTime" : { "ts" : Timestamp(1605011261, 1), "t" : NumberLong(1) }, "durableOpTime" : { "ts" : Timestamp(1605011261, 1), "t" : NumberLong(1) }, "lastAppliedWallTime" : ISODate("2020-11-10T12:27:41.873Z"), "lastDurableWallTime" : ISODate("2020-11-10T12:27:41.873Z") }, "electionCandidateMetrics" : { "lastElectionReason" : "electionTimeout", "lastElectionDate" : ISODate("2020-11-10T12:20:21.632Z"), "electionTerm" : NumberLong(1), "lastCommittedOpTimeAtElection" : { "ts" : Timestamp(0, 0), "t" : NumberLong(-1) }, "lastSeenOpTimeAtElection" : { "ts" : Timestamp(1605010821, 1), "t" : NumberLong(-1) }, "numVotesNeeded" : 1, "priorityAtElection" : 1, "electionTimeoutMillis" : NumberLong(10000), "newTermStartDate" : ISODate("2020-11-10T12:20:21.694Z"), "wMajorityWriteAvailabilityDate" : ISODate("2020-11-10T12:20:21.719Z") }, "members" : [ { "_id" : 0, "name" : "node01.test.org:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 1107, "optime" : { "ts" : Timestamp(1605011261, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2020-11-10T12:27:41Z"), "syncSourceHost" : "", "syncSourceId" : -1, "infoMessage" : "", "electionTime" : Timestamp(1605010821, 2), "electionDate" : ISODate("2020-11-10T12:20:21Z"), "configVersion" : 3, "configTerm" : 1, "self" : true, "lastHeartbeatMessage" : "" }, { "_id" : 1, "name" : "node02:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 21, "optime" : { "ts" : Timestamp(1605011261, 1), "t" : NumberLong(1) }, "optimeDurable" : { "ts" : Timestamp(1605011261, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2020-11-10T12:27:41Z"), "optimeDurableDate" : ISODate("2020-11-10T12:27:41Z"), "lastHeartbeat" : ISODate("2020-11-10T12:27:44.967Z"), "lastHeartbeatRecv" : ISODate("2020-11-10T12:27:43.983Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "", "syncSourceHost" : "node01.test.org:27017", "syncSourceId" : 0, "infoMessage" : "", "configVersion" : 3, "configTerm" : 1 }, { "_id" : 2, "name" : "node03:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 14, "optime" : { "ts" : Timestamp(1605011261, 1), "t" : NumberLong(1) }, "optimeDurable" : { "ts" : Timestamp(1605011261, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2020-11-10T12:27:41Z"), "optimeDurableDate" : ISODate("2020-11-10T12:27:41Z"), "lastHeartbeat" : ISODate("2020-11-10T12:27:44.967Z"), "lastHeartbeatRecv" : ISODate("2020-11-10T12:27:44.571Z"), "pingMs" : NumberLong(1), "lastHeartbeatMessage" : "", "syncSourceHost" : "node02:27017", "syncSourceId" : 1, "infoMessage" : "", "configVersion" : 3, "configTerm" : 1 } ], "ok" : 1, "$clusterTime" : { "clusterTime" : Timestamp(1605011261, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } }, "operationTime" : Timestamp(1605011261, 1) } test_replset:PRIMARY>
提示:添加節點到副本成員使用rs.add(),括號里面寫主機名,或者ip地址,如果對應節點監聽端口不是27017,還需要寫明端口;除此我們也可以直接用一個子文檔的形式,手動指定節點的各屬性信息來添加節點到副本集成員;
到此副本集的配置就完成了,三個節點都添加到副本集;
驗證:在主庫上插入數據,看看從庫是否都可以正常同步其數據呢?
在主節點插入數據
test_replset:PRIMARY> use students switched to db students test_replset:PRIMARY> db.student_info.insert({name:"tom",age:18,gender:"M"}) WriteResult({ "nInserted" : 1 }) test_replset:PRIMARY> db.student_info.find() { "_id" : ObjectId("5faa89297077300f4fc31d1c"), "name" : "tom", "age" : 18, "gender" : "M" } test_replset:PRIMARY>
在從節點查看數據
test_replset:SECONDARY> show dbs uncaught exception: Error: listDatabases failed:{ "topologyVersion" : { "processId" : ObjectId("5faa83025f9218c5d19ea91b"), "counter" : NumberLong(4) }, "operationTime" : Timestamp(1605011801, 1), "ok" : 0, "errmsg" : "not master and slaveOk=false", "code" : 13435, "codeName" : "NotMasterNoSlaveOk", "$clusterTime" : { "clusterTime" : Timestamp(1605011801, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } } : _getErrorWithCode@src/mongo/shell/utils.js:25:13 Mongo.prototype.getDBs/<@src/mongo/shell/mongo.js:147:19 Mongo.prototype.getDBs@src/mongo/shell/mongo.js:99:12 shellHelper.show@src/mongo/shell/utils.js:937:13 shellHelper@src/mongo/shell/utils.js:819:15 @(shellhelp2):1:1 test_replset:SECONDARY>
提示:這里在從節點上沒法查看數據,原因是默認情況副本集配置好以后,要在從節點運行rs.secondaryOk()來告訴從節點已經配置好了,否則它會不允許我們讀,有點類似從節點為就緒的感覺;
在從節點上運行rs.secondaryOk(),然后再次查看數據
test_replset:SECONDARY> rs.secondaryOk() test_replset:SECONDARY> show dbs admin 0.000GB config 0.000GB local 0.000GB students 0.000GB test_replset:SECONDARY> use students switched to db students test_replset:SECONDARY> show collections student_info test_replset:SECONDARY> db.student_info.find().pretty() { "_id" : ObjectId("5faa89297077300f4fc31d1c"), "name" : "tom", "age" : 18, "gender" : "M" } test_replset:SECONDARY>
提示:可以看到在secondary節點上能夠查詢到對應數據庫中的對應collection中插入的數據;
驗證:在從節點上插入數據,看看是否能插入成功?
test_replset:SECONDARY> db.student_info.insert({name:"jerry",age:19,gender:"M"}) WriteCommandError({ "topologyVersion" : { "processId" : ObjectId("5faa831bde03c4ae108f0be4"), "counter" : NumberLong(3) }, "operationTime" : Timestamp(1605012904, 1), "ok" : 0, "errmsg" : "not master", "code" : 10107, "codeName" : "NotMaster", "$clusterTime" : { "clusterTime" : Timestamp(1605012904, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }) test_replset:SECONDARY>
提示:我們在從節點上插入數據,它提示我們不是主節點,不允許插入數據;這說明副本集的從節點是不允許寫;
驗證:將主節點上的mongodb服務停掉,然后在其他兩個節點上查看副本集狀態,看看兩個節點是否重新選舉了master?
[root@node01 ~]# systemctl stop mongod.service [root@node01 ~]# ss -tnl State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 *:22 *:* LISTEN 0 100 127.0.0.1:25 *:* LISTEN 0 128 :::22 :::* LISTEN 0 100 ::1:25 :::* [root@node01 ~]# mongo --host node02 MongoDB shell version v4.4.1 connecting to: mongodb://node02:27017/?compressors=disabled&gssapiServiceName=mongodb Implicit session: session { "id" : UUID("90d60f76-fc95-44a8-bbf7-d927d5947331") } MongoDB server version: 4.4.1 --- The server generated these startup warnings when booting: 2020-11-10T20:09:39.555+08:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted 2020-11-10T20:09:39.555+08:00: /sys/kernel/mm/transparent_hugepage/enabled is 'always'. We suggest setting it to 'never' 2020-11-10T20:09:39.555+08:00: /sys/kernel/mm/transparent_hugepage/defrag is 'always'. We suggest setting it to 'never' --- --- Enable MongoDB's free cloud-based monitoring service, which will then receive and display metrics about your deployment (disk utilization, CPU, operation statistics, etc). The monitoring data will be available on a MongoDB website with a unique URL accessible to you and anyone you share the URL with. MongoDB may use this information to make product improvements and to suggest MongoDB products and deployment options to you. To enable free monitoring, run the following command: db.enableFreeMonitoring() To permanently disable this reminder, run the following command: db.disableFreeMonitoring() --- test_replset:PRIMARY> rs.status() { "set" : "test_replset", "date" : ISODate("2020-11-10T12:59:25.775Z"), "myState" : 1, "term" : NumberLong(2), "syncSourceHost" : "", "syncSourceId" : -1, "heartbeatIntervalMillis" : NumberLong(2000), "majorityVoteCount" : 2, "writeMajorityCount" : 2, "votingMembersCount" : 3, "writableVotingMembersCount" : 3, "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1605013164, 1), "t" : NumberLong(2) }, "lastCommittedWallTime" : ISODate("2020-11-10T12:59:24.417Z"), "readConcernMajorityOpTime" : { "ts" : Timestamp(1605013164, 1), "t" : NumberLong(2) }, "readConcernMajorityWallTime" : ISODate("2020-11-10T12:59:24.417Z"), "appliedOpTime" : { "ts" : Timestamp(1605013164, 1), "t" : NumberLong(2) }, "durableOpTime" : { "ts" : Timestamp(1605013164, 1), "t" : NumberLong(2) }, "lastAppliedWallTime" : ISODate("2020-11-10T12:59:24.417Z"), "lastDurableWallTime" : ISODate("2020-11-10T12:59:24.417Z") }, "electionCandidateMetrics" : { "lastElectionReason" : "stepUpRequestSkipDryRun", "lastElectionDate" : ISODate("2020-11-10T12:58:34.398Z"), "electionTerm" : NumberLong(2), "lastCommittedOpTimeAtElection" : { "ts" : Timestamp(1605013112, 1), "t" : NumberLong(1) }, "lastSeenOpTimeAtElection" : { "ts" : Timestamp(1605013112, 1), "t" : NumberLong(1) }, "numVotesNeeded" : 2, "priorityAtElection" : 1, "electionTimeoutMillis" : NumberLong(10000), "priorPrimaryMemberId" : 0, "numCatchUpOps" : NumberLong(0), "newTermStartDate" : ISODate("2020-11-10T12:58:34.410Z"), "wMajorityWriteAvailabilityDate" : ISODate("2020-11-10T12:58:34.506Z") }, "members" : [ { "_id" : 0, "name" : "node01.test.org: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("2020-11-10T12:59:24.558Z"), "lastHeartbeatRecv" : ISODate("2020-11-10T12:58:35.045Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "Error connecting to node01.test.org:27017 (192.168.0.41:27017) :: caused by :: Connection refused", "syncSourceHost" : "", "syncSourceId" : -1, "infoMessage" : "", "configVersion" : 3, "configTerm" : 1 }, { "_id" : 1, "name" : "node02:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 2987, "optime" : { "ts" : Timestamp(1605013164, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2020-11-10T12:59:24Z"), "syncSourceHost" : "", "syncSourceId" : -1, "infoMessage" : "", "electionTime" : Timestamp(1605013114, 1), "electionDate" : ISODate("2020-11-10T12:58:34Z"), "configVersion" : 3, "configTerm" : 2, "self" : true, "lastHeartbeatMessage" : "" }, { "_id" : 2, "name" : "node03:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 1914, "optime" : { "ts" : Timestamp(1605013164, 1), "t" : NumberLong(2) }, "optimeDurable" : { "ts" : Timestamp(1605013164, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2020-11-10T12:59:24Z"), "optimeDurableDate" : ISODate("2020-11-10T12:59:24Z"), "lastHeartbeat" : ISODate("2020-11-10T12:59:24.459Z"), "lastHeartbeatRecv" : ISODate("2020-11-10T12:59:25.506Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "", "syncSourceHost" : "node02:27017", "syncSourceId" : 1, "infoMessage" : "", "configVersion" : 3, "configTerm" : 2 } ], "ok" : 1, "$clusterTime" : { "clusterTime" : Timestamp(1605013164, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } }, "operationTime" : Timestamp(1605013164, 1) } test_replset:PRIMARY>
提示:可以看到當node01上的mongodb服務停掉以后,對應的主節點也就不能夠正常的向從節點發送心跳,所以剩下的兩個從節點都認為主節點掛了,此時他倆就開始重新選舉主節點;我們再次連接到node02上的mongodb查看副本集狀態時,此時node02上的mongodb就成為了主節點;同時我們也能看到node01的健康狀態為0,表示不健康;
查看從節點的復制信息
test_replset:PRIMARY> rs.printSecondaryReplicationInfo() source: node01.test.org:27017 syncedTo: Thu Jan 01 1970 08:00:00 GMT+0800 (CST) 1605013534 secs (445837.09 hrs) behind the primary source: node03:27017 syncedTo: Tue Nov 10 2020 21:05:34 GMT+0800 (CST) 0 secs (0 hrs) behind the primary test_replset:PRIMARY>
提示:這里可以看到node01落后主節點很長時間,node03不落后主節點;
查看同步信息時間窗口,以及oplog大小
test_replset:PRIMARY> rs.printReplicationInfo() configured oplog size: 2048MB log length start to end: 2291secs (0.64hrs) oplog first event time: Tue Nov 10 2020 20:27:23 GMT+0800 (CST) oplog last event time: Tue Nov 10 2020 21:05:34 GMT+0800 (CST) now: Tue Nov 10 2020 21:05:35 GMT+0800 (CST) test_replset:PRIMARY>
提示:可以看到oplog的小為2048MB,日志開始到結束的時間窗口為2291秒,意思是說當前啟動副本集到查詢復制信息是的時長為2291秒;第一個oplog時間時間和最后oplog事件時間以及現在的時間都可查看得到;
移除節點(從副本集中,將node01節點移除)
test_replset:PRIMARY> rs.remove("node01.test.org:27017") { "ok" : 1, "$clusterTime" : { "clusterTime" : Timestamp(1605014063, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } }, "operationTime" : Timestamp(1605014063, 1) } test_replset:PRIMARY> rs.status() { "set" : "test_replset", "date" : ISODate("2020-11-10T13:14:35.362Z"), "myState" : 1, "term" : NumberLong(2), "syncSourceHost" : "", "syncSourceId" : -1, "heartbeatIntervalMillis" : NumberLong(2000), "majorityVoteCount" : 2, "writeMajorityCount" : 2, "votingMembersCount" : 2, "writableVotingMembersCount" : 2, "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1605014074, 1), "t" : NumberLong(2) }, "lastCommittedWallTime" : ISODate("2020-11-10T13:14:34.514Z"), "readConcernMajorityOpTime" : { "ts" : Timestamp(1605014074, 1), "t" : NumberLong(2) }, "readConcernMajorityWallTime" : ISODate("2020-11-10T13:14:34.514Z"), "appliedOpTime" : { "ts" : Timestamp(1605014074, 1), "t" : NumberLong(2) }, "durableOpTime" : { "ts" : Timestamp(1605014074, 1), "t" : NumberLong(2) }, "lastAppliedWallTime" : ISODate("2020-11-10T13:14:34.514Z"), "lastDurableWallTime" : ISODate("2020-11-10T13:14:34.514Z") }, "electionCandidateMetrics" : { "lastElectionReason" : "stepUpRequestSkipDryRun", "lastElectionDate" : ISODate("2020-11-10T12:58:34.398Z"), "electionTerm" : NumberLong(2), "lastCommittedOpTimeAtElection" : { "ts" : Timestamp(1605013112, 1), "t" : NumberLong(1) }, "lastSeenOpTimeAtElection" : { "ts" : Timestamp(1605013112, 1), "t" : NumberLong(1) }, "numVotesNeeded" : 2, "priorityAtElection" : 1, "electionTimeoutMillis" : NumberLong(10000), "priorPrimaryMemberId" : 0, "numCatchUpOps" : NumberLong(0), "newTermStartDate" : ISODate("2020-11-10T12:58:34.410Z"), "wMajorityWriteAvailabilityDate" : ISODate("2020-11-10T12:58:34.506Z") }, "members" : [ { "_id" : 1, "name" : "node02:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 3897, "optime" : { "ts" : Timestamp(1605014074, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2020-11-10T13:14:34Z"), "syncSourceHost" : "", "syncSourceId" : -1, "infoMessage" : "", "electionTime" : Timestamp(1605013114, 1), "electionDate" : ISODate("2020-11-10T12:58:34Z"), "configVersion" : 4, "configTerm" : 2, "self" : true, "lastHeartbeatMessage" : "" }, { "_id" : 2, "name" : "node03:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 2824, "optime" : { "ts" : Timestamp(1605014063, 1), "t" : NumberLong(2) }, "optimeDurable" : { "ts" : Timestamp(1605014063, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2020-11-10T13:14:23Z"), "optimeDurableDate" : ISODate("2020-11-10T13:14:23Z"), "lastHeartbeat" : ISODate("2020-11-10T13:14:33.958Z"), "lastHeartbeatRecv" : ISODate("2020-11-10T13:14:33.970Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "", "syncSourceHost" : "node02:27017", "syncSourceId" : 1, "infoMessage" : "", "configVersion" : 4, "configTerm" : 2 } ], "ok" : 1, "$clusterTime" : { "clusterTime" : Timestamp(1605014074, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } }, "operationTime" : Timestamp(1605014074, 1) } test_replset:PRIMARY>
提示:移除節點需要寫明對應節點的主機名地址和端口,或者是ip地址端口;這個字串必須是同加入到副本集中的名字相同才可移除,否則它會提示我們給定主機字符串找不到;
設置node03的優先級為10
test_replset:PRIMARY> cfg = rs.conf() { "_id" : "test_replset", "version" : 4, "term" : 2, "protocolVersion" : NumberLong(1), "writeConcernMajorityJournalDefault" : true, "members" : [ { "_id" : 1, "host" : "node02:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 2, "host" : "node03:27017", "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("5faa85853d5a2d8fdf8af85e") } } test_replset:PRIMARY> cfg.members[1].priority = 10 10 test_replset:PRIMARY> rs.reconfig(cfg) { "ok" : 1, "$clusterTime" : { "clusterTime" : Timestamp(1605015945, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } }, "operationTime" : Timestamp(1605015945, 1) } test_replset:PRIMARY> rs.config() { "_id" : "test_replset", "version" : 5, "term" : 3, "protocolVersion" : NumberLong(1), "writeConcernMajorityJournalDefault" : true, "members" : [ { "_id" : 1, "host" : "node02:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 2, "host" : "node03:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 10, "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("5faa85853d5a2d8fdf8af85e") } } test_replset:SECONDARY>
提示:設置某個成員的優先級,首先要把當前配置用一個變量保存,然后通過修改保存的配置變量來修改優先級以后,再使用rs.reconfig()來讀取修改后的配置即可生效;這里需要注意我在修改配置文件時指定成員,是指定對應成員在members列表的下標,而非id;所以修改前要先確定對應成員的下標是多少;我們修改了node03的優先級以后,心細的你一定會發現node02此時就變成了secondary;這意味着,只要在副本集中有更高優先級的成員加入,會觸發一次選舉,當然選舉成為主節點,影響最大的就是各節點的優先級,優先級越高,當選主節點的幾率就越大;默認情況每個成員的優先級都為1,優先級設定的取值范圍是0-1000;
將node01上的mongodb服務啟動起來,然后在主節點上將其添加為仲裁節點
啟動node01上的mongodb服務
[root@node01 ~]# systemctl start mongod.service [root@node01 ~]# ss -tnl State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 *:22 *:* LISTEN 0 100 127.0.0.1:25 *:* LISTEN 0 128 *:27017 *:* LISTEN 0 128 :::22 :::* LISTEN 0 100 ::1:25 :::* [root@node01 ~]#
在主節點上將node01添加為仲裁節點
test_replset:PRIMARY> rs.addArb("node01") { "ok" : 1, "$clusterTime" : { "clusterTime" : Timestamp(1605016848, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } }, "operationTime" : Timestamp(1605016848, 1) } test_replset:PRIMARY>
查看配置信息,看看node01上的arbiterOnly是否變成了true?
test_replset:PRIMARY> rs.config() { "_id" : "test_replset", "version" : 8, "term" : 3, "protocolVersion" : NumberLong(1), "writeConcernMajorityJournalDefault" : true, "members" : [ { "_id" : 1, "host" : "node02:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 2, "host" : "node03:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 10, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 3, "host" : "node01:27017", "arbiterOnly" : true, "buildIndexes" : true, "hidden" : false, "priority" : 0, "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("5faa85853d5a2d8fdf8af85e") } } test_replset:PRIMARY>
提示:可以看到node01節點上的arbiterOnly屬性已經啟用,說明這就是一個仲裁節點;我們說過仲裁節點上沒有保存副本,我們到node01上看看是否保存的有副本集數據呢?
test_replset:ARBITER> show databases uncaught exception: Error: listDatabases failed:{ "topologyVersion" : { "processId" : ObjectId("5faa9d87e504fa222c4a584d"), "counter" : NumberLong(1) }, "ok" : 0, "errmsg" : "not master and slaveOk=false", "code" : 13435, "codeName" : "NotMasterNoSlaveOk" } : _getErrorWithCode@src/mongo/shell/utils.js:25:13 Mongo.prototype.getDBs/<@src/mongo/shell/mongo.js:147:19 Mongo.prototype.getDBs@src/mongo/shell/mongo.js:99:12 shellHelper.show@src/mongo/shell/utils.js:937:13 shellHelper@src/mongo/shell/utils.js:819:15 @(shellhelp2):1:1 test_replset:ARBITER> rs.secondaryOk() test_replset:ARBITER> show databases uncaught exception: Error: listDatabases failed:{ "topologyVersion" : { "processId" : ObjectId("5faa9d87e504fa222c4a584d"), "counter" : NumberLong(1) }, "ok" : 0, "errmsg" : "node is not in primary or recovering state", "code" : 13436, "codeName" : "NotMasterOrSecondary" } : _getErrorWithCode@src/mongo/shell/utils.js:25:13 Mongo.prototype.getDBs/<@src/mongo/shell/mongo.js:147:19 Mongo.prototype.getDBs@src/mongo/shell/mongo.js:99:12 shellHelper.show@src/mongo/shell/utils.js:937:13 shellHelper@src/mongo/shell/utils.js:819:15 @(shellhelp2):1:1 test_replset:ARBITER>
提示:我們在仲裁節點上查看數據庫列表,它提示我們不是主節點和從節點,不允許讀;既然都不允許讀,寫肯定是沒有辦法進行;
ok,到此mongodb的副本集的配置、測試就到此為止;