1. 程式人生 > 實用技巧 >Mongodb的副本集

Mongodb的副本集

副本集-Replica Sets

1.1 簡介

MongoDB中的副本集(Replica Set)是一組維護相同資料集的mongod服務。 副本集可提供冗餘和高可用性,是所有生產部署的基礎。
也可以說,副本集類似於有自動故障恢復功能的主從叢集。通俗的講就是用多臺機器進行同一資料的非同步同步,從而使多臺機器擁有同一資料的多個副本,並且當主庫當掉時在不需要使用者干預的情況下自動切換其他備份伺服器做主庫。而且還可以利用副本伺服器做只讀伺服器,實現讀寫分離,提高負載。
(1)冗餘和資料可用性
複製提供冗餘並提高資料可用性。 通過在不同資料庫伺服器上提供多個數據副本,複製可提供一定級別的容錯功能,以防止丟失單個數據庫伺服器。
在某些情況下,複製可以提供增加的讀取效能,因為客戶端可以將讀取操作傳送到不同的服務上, 在不同資料中心維護資料副本可以增加分散式應用程式的資料位置和可用性。 您還可以為專用目的維護其他副本,例如災難恢復,報告或備份。
(2)MongoDB中的複製


副本集是一組維護相同資料集的mongod例項。 副本集包含多個數據承載節點和可選的一個仲裁節點。在承載資料的節點中,一個且僅一個成員被視為主節點,而其他節點被視為次要(從)節點。
主節點接收所有寫操作。 副本集只能有一個主要能夠確認具有{w:“most”}寫入關注的寫入; 雖然在某些情況下,另一個mongod例項可能暫時認為自己也是主要的。主要記錄其操作日誌中的資料集的所有更改,即oplog。

輔助(副本)節點複製主節點的oplog並將操作應用於其資料集,以使輔助節點的資料集反映主節點的資料集。 如果主要人員不在,則符合條件的中學將舉行選舉以選出新的主要人員。
(3)主從複製和副本集區別
主從叢集和副本集最大的區別就是副本集沒有固定的“主節點”;整個叢集會選出一個“主節點”,當其掛掉後,又在剩下的從節點中選中其他節點為“主節點”,副本集總有一個活躍點(主、primary)和一個或多個備份節點(從、secondary)。

1.2 副本集的三個角色

副本集有兩種型別三種角色
兩種型別:
主節點( Primary)型別:資料操作的主要連線點,可讀寫。
次要(輔助、從)節點( Secondaries)型別:資料冗餘備份節點,可以讀或選舉。
三種角色:
主要成員(Primary):主要接收所有寫操作。就是主節點。
副本成員(Replicate):從主節點通過複製操作以維護相同的資料集,即備份資料,不可寫操作,但可以讀操作(但需要配置)。是預設的一種從節點型別。

仲裁者( Arbiter):不保留任何資料的副本,只具有投票選舉作用。當然也可以將仲裁伺服器維護為副本集的一部分,即副本成員同時也可以是仲裁者。也是一種從節點型別。

關於仲裁者的額外說明:
您可以將額外的mongod例項新增到副本集作為仲裁者。 仲裁者不維護資料集。 仲裁者的目的是通過響應其他副本整合員的心跳和選舉請求來維護副本集中的仲裁。 因為它們不儲存資料集,所以仲裁器可以是提供副本集仲裁功能的好方法,其資源成本比具有資料集的全功能副本整合員更便宜。
如果您的副本集具有偶數個成員,請新增仲裁者以獲得主要選舉中的“大多數”投票。 仲裁者不需要專用硬體。仲裁者將永遠是仲裁者,而主要人員可能會退出併成為次要人員,而次要人員可能成為選舉期間的主要人員。如果你的副本+主節點的個數是偶數,建議加一個仲裁者,形成奇數,容易滿足大多數的投票。如果你的副本+主節點的個數是奇數,可以不加仲裁者。

1.3 副本集架構目標

一主一副本一仲裁

1.4 副本集的建立

1.4.1 第一步:建立主節點

建立存放資料和日誌的目錄

[root@localhost ~]# mkdir -p /mongodb/replica_sets/myrs_27017/log
[root@localhost ~]# mkdir -p /mongodb/replica_sets/myrs_27017/data/db
[root@localhost ~]# tar -xvf mongodb-linux-x86_64-4.0.10.tgz
mongodb-linux-x86_64-4.0.10/THIRD-PARTY-NOTICES.gotools
mongodb-linux-x86_64-4.0.10/README
mongodb-linux-x86_64-4.0.10/THIRD-PARTY-NOTICES
mongodb-linux-x86_64-4.0.10/MPL-2
mongodb-linux-x86_64-4.0.10/LICENSE-Community.txt
mongodb-linux-x86_64-4.0.10/bin/mongodump
mongodb-linux-x86_64-4.0.10/bin/mongorestore
mongodb-linux-x86_64-4.0.10/bin/mongoexport
mongodb-linux-x86_64-4.0.10/bin/mongoimport
mongodb-linux-x86_64-4.0.10/bin/mongostat
mongodb-linux-x86_64-4.0.10/bin/mongotop
mongodb-linux-x86_64-4.0.10/bin/bsondump
mongodb-linux-x86_64-4.0.10/bin/mongofiles
mongodb-linux-x86_64-4.0.10/bin/mongoreplay
mongodb-linux-x86_64-4.0.10/bin/mongod
mongodb-linux-x86_64-4.0.10/bin/mongos
mongodb-linux-x86_64-4.0.10/bin/mongo
mongodb-linux-x86_64-4.0.10/bin/install_compass
[root@localhost ~]# mv mongodb-linux-x86_64-4.0.10 /usr/local/mongodb
[root@localhost ~]# ll
總用量 83012
-rw-------. 1 root root     1242 10月  1 19:39 anaconda-ks.cfg
-rw-r--r--. 1 root root 84996443 10月 12 17:25 mongodb-linux-x86_64-4.0.10.tgz

新建或修改配置檔案:

[root@localhost ~]# vim /mongodb/replica_sets/myrs_27017/mongod.conf
[root@localhost ~]# cat /mongodb/replica_sets/myrs_27017/mongod.conf
systemLog:
 #MongoDB傳送所有日誌輸出的目標指定為檔案
 destination: file
 #mongod或mongos應向其傳送所有診斷日誌記錄資訊的日誌檔案的路徑
 path: "/mongodb/replica_sets/myrs_27017/log/mongod.log"
 #當mongos或mongod例項重新啟動時,mongos或mongod會將新條目附加到現有日誌檔案的末尾。
 logAppend: true
storage:
 #mongod例項儲存其資料的目錄。storage.dbPath設定僅適用於mongod。
 dbPath: "/mongodb/replica_sets/myrs_27017/data/db"
 journal:
   #啟用或禁用永續性日誌以確保資料檔案保持有效和可恢復。
   enabled: true
processManagement:
 #啟用在後臺執行mongos或mongod程序的守護程序模式。
 fork: true
 #指定用於儲存mongos或mongod程序的程序ID的檔案位置,其中mongos或mongod將寫入其PID
 pidFilePath: "/mongodb/replica_sets/myrs_27017/log/mongod.pid"
net:
 #服務例項繫結所有IP,有副作用,副本集初始化的時候,節點名字會自動設定為本地域名,而不是ip
 #bindIpAll: true
 #服務例項繫結的IP
 bindIp: localhost
 #bindIp
 #繫結的埠
 port: 27017
replication:
 #副本集的名稱
 replSetName: myrs
[root@localhost ~]#

啟動節點服務:

[root@localhost ~]# /usr/local/mongodb/bin/mongod -f  /mongodb/replica_sets/myrs_27017/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 11308
child process started successfully, parent exiting
1.4.2 第二步:建立副本節點

建立存放資料和日誌的目錄

[root@localhost ~]# mkdir -p /mongodb/replica_sets/myrs_27018/log
[root@localhost ~]# mkdir -p /mongodb/replica_sets/myrs_27018/data/db
[root@localhost ~]# vim /mongodb/replica_sets/myrs_27018/mongod.conf
[root@localhost ~]# /usr/local/mongodb/bin/mongod -f  /mongodb/replica_sets/myrs_27018/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 12236
child process started successfully, parent exiting
[root@localhost ~]# cat /mongodb/replica_sets/myrs_27018/mongod.conf
systemLog:
 #MongoDB傳送所有日誌輸出的目標指定為檔案
 destination: file
 #mongod或mongos應向其傳送所有診斷日誌記錄資訊的日誌檔案的路徑
 path: "/mongodb/replica_sets/myrs_27018/log/mongod.log"
 #當mongos或mongod例項重新啟動時,mongos或mongod會將新條目附加到現有日誌檔案的末尾。
 logAppend: true
storage:
 #mongod例項儲存其資料的目錄。storage.dbPath設定僅適用於mongod。
 dbPath: "/mongodb/replica_sets/myrs_27018/data/db"
 journal:
   #啟用或禁用永續性日誌以確保資料檔案保持有效和可恢復。
   enabled: true
processManagement:
 #啟用在後臺執行mongos或mongod程序的守護程序模式。
 fork: true
 #指定用於儲存mongos或mongod程序的程序ID的檔案位置,其中mongos或mongod將寫入其PID
 pidFilePath: "/mongodb/replica_sets/myrs_27018/log/mongod.pid"
net:
 #服務例項繫結所有IP,有副作用,副本集初始化的時候,節點名字會自動設定為本地域名,而不是ip
 #bindIpAll: true
 #服務例項繫結的IP
 bindIp: localhost
 #bindIp
 #繫結的埠
 port: 27018
replication:
 #副本集的名稱
 replSetName: myrs
[root@localhost ~]#

1.4.3 第三步:建立仲裁節點

建立存放資料和日誌的目錄

[root@localhost ~]# mkdir -p /mongodb/replica_sets/myrs_27019/log \ &
[2] 12894
[root@localhost ~]# mkdir -p /mongodb/replica_sets/myrs_27019/data/db
[2]-  完成                  mkdir -p /mongodb/replica_sets/myrs_27019/log \
[root@localhost ~]# vim /mongodb/replica_sets/myrs_27019/mongod.conf
[root@localhost ~]# /usr/local/mongodb/bin/mongod -f  /mongodb/replica_sets/myrs_27019/mongod.conf
Unrecognized option: ystemLog.destination
try '/usr/local/mongodb/bin/mongod --help' for more information
[root@localhost ~]# vim /mongodb/replica_sets/myrs_27019/mongod.conf
[root@localhost ~]# /usr/local/mongodb/bin/mongod -f  /mongodb/replica_sets/myrs_27019/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 13273
child process started successfully, parent exiting
[root@localhost ~]# cat /mongodb/replica_sets/myrs_27019/mongod.conf
systemLog:
 #MongoDB傳送所有日誌輸出的目標指定為檔案
 destination: file
 #mongod或mongos應向其傳送所有診斷日誌記錄資訊的日誌檔案的路徑
 path: "/mongodb/replica_sets/myrs_27019/log/mongod.log"
 #當mongos或mongod例項重新啟動時,mongos或mongod會將新條目附加到現有日誌檔案的末尾。
 logAppend: true
storage:
 #mongod例項儲存其資料的目錄。storage.dbPath設定僅適用於mongod。
 dbPath: "/mongodb/replica_sets/myrs_27019/data/db"
 journal:
   #啟用或禁用永續性日誌以確保資料檔案保持有效和可恢復。
   enabled: true
processManagement:
 #啟用在後臺執行mongos或mongod程序的守護程序模式。
 fork: true
 #指定用於儲存mongos或mongod程序的程序ID的檔案位置,其中mongos或mongod將寫入其PID
 pidFilePath: "/mongodb/replica_sets/myrs_27019/log/mongod.pid"
net:
 #服務例項繫結所有IP,有副作用,副本集初始化的時候,節點名字會自動設定為本地域名,而不是ip
 #bindIpAll: true
 #服務例項繫結的IP
 bindIp: localhost
 #bindIp
 #繫結的埠
 port: 27019
replication:
 #副本集的名稱
 replSetName: myrs
[root@localhost ~]#
1.4.5 第四步:初始化配置副本集和主節點

使用客戶端命令連線任意一個節點,但這裡儘量要連線主節點(27017節點):

[root@localhost ~]# /usr/local/mongodb/bin/mongo  --port=27017
MongoDB shell version v4.0.10
connecting to: mongodb://127.0.0.1:27017/?gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("05371b65-11b8-4fd3-a359-7b5dd88d8103") }
MongoDB server version: 4.0.10
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
        http://docs.mongodb.org/
Questions? Try the support group
        http://groups.google.com/group/mongodb-user
Server has startup warnings:
2020-10-12T17:51:28.979+0800 I CONTROL  [initandlisten]
2020-10-12T17:51:28.979+0800 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2020-10-12T17:51:28.979+0800 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2020-10-12T17:51:28.979+0800 I CONTROL  [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
2020-10-12T17:51:28.979+0800 I CONTROL  [initandlisten]
2020-10-12T17:51:28.980+0800 I CONTROL  [initandlisten]
2020-10-12T17:51:28.980+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2020-10-12T17:51:28.980+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2020-10-12T17:51:28.980+0800 I CONTROL  [initandlisten]
2020-10-12T17:51:28.980+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
2020-10-12T17:51:28.980+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2020-10-12T17:51:28.980+0800 I CONTROL  [initandlisten]

果,連線上之後,很多命令無法使用,,比如 show dbs 等,必須初始化副本集才行
準備初始化新的副本集:
語法:

rs.initiate(configuration)
Parameter Type Description
configuration document Optional. A document that specifies configuration for the new replica set. If a configuration is not specified, MongoDB uses a default replica set configuration.

【示例】
使用預設的配置來初始化副本集:

rs.initiate()

執行結果:

> rs.initiate()
{
        "info2" : "no configuration specified. Using a default configuration for the set",
        "me" : "localhost:27017",
        "ok" : 1,
        "operationTime" : Timestamp(1602496641, 1),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1602496641, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
}
myrs:SECONDARY>
myrs:PRIMARY>
myrs:PRIMARY>
myrs:PRIMARY>
myrs:PRIMARY>
myrs:PRIMARY>

提示:
1)“ok”的值為1,說明建立成功。
2)命令列提示符發生變化,變成了一個從節點角色,此時預設不能讀寫。稍等片刻,回車,變成主節點。

1.4.6 第五步:檢視副本集的配置內容

說明:
返回包含當前副本集配置的文件。
語法:

rs.conf(configuration)

rs.config() 是該方法的別名。
configuration:可選,如果沒有配置,則使用預設主節點配置。

【示例】
在27017上執行副本集中當前節點的預設節點配置

myrs:PRIMARY> rs.conf()
{
        "_id" : "myrs",
        "version" : 1,
        "protocolVersion" : NumberLong(1),
        "writeConcernMajorityJournalDefault" : true,
        "members" : [
                {
                        "_id" : 0,
                        "host" : "localhost: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("5f84288139869ebb123f25db")
        }
}

說明:
1) "_id" : "myrs" :副本集的配置資料儲存的主鍵值,預設就是副本集的名字
2) "members" :副本整合員陣列,此時只有一個: "host" : "180.76.159.126:27017" ,該成員不是仲裁節點: "arbiterOnly" : false ,優先順序(權重值): "priority" : 1,
3) "settings" :副本集的引數配置。

提示:副本集配置的檢視命令,本質是查詢的是 system.replset 的表中的資料:

myrs:PRIMARY>  use local
switched to db local
myrs:PRIMARY> how collections
2020-10-12T20:22:22.925+0800 E QUERY    [js] SyntaxError: missing ; before statement @(shell):1:4
myrs:PRIMARY> show collections
oplog.rs
replset.election
replset.minvalid
replset.oplogTruncateAfterPoint
startup_log
system.replset
system.rollback.id
myrs:PRIMARY> db.system.replset.find()
{ "_id" : "myrs", "version" : 1, "protocolVersion" : NumberLong(1), "writeConcernMajorityJournalDefault" : true, "members" : [ { "_id" : 0, "host" : "localhost: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("5f84288139869ebb123f25db") } }

1.4.7 第六步:檢視副本集狀態

檢查副本集狀態。
說明:
返回包含狀態資訊的文件。此輸出使用從副本集的其他成員傳送的心跳包中獲得的資料反映副本集的當
前狀態。
語法:

rs.status()

【示例】
在27017上檢視副本集狀態:

myrs:PRIMARY> rs.status()
{
        "set" : "myrs",
        "date" : ISODate("2020-10-12T12:22:54.806Z"),
        "myState" : 1,
        "term" : NumberLong(1),
        "syncingTo" : "",
        "syncSourceHost" : "",
        "syncSourceId" : -1,
        "heartbeatIntervalMillis" : NumberLong(2000),
        "optimes" : {
                "lastCommittedOpTime" : {
                        "ts" : Timestamp(1602505374, 1),
                        "t" : NumberLong(1)
                },
                "readConcernMajorityOpTime" : {
                        "ts" : Timestamp(1602505374, 1),
                        "t" : NumberLong(1)
                },
                "appliedOpTime" : {
                        "ts" : Timestamp(1602505374, 1),
                        "t" : NumberLong(1)
                },
                "durableOpTime" : {
                        "ts" : Timestamp(1602505374, 1),
                        "t" : NumberLong(1)
                }
        },
        "lastStableCheckpointTimestamp" : Timestamp(1602505344, 1),
        "members" : [
                {
                        "_id" : 0,
                        "name" : "localhost:27017",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "uptime" : 9087,
                        "optime" : {
                                "ts" : Timestamp(1602505374, 1),
                                "t" : NumberLong(1)
                        },
                        "optimeDate" : ISODate("2020-10-12T12:22:54Z"),
                        "syncingTo" : "",
                        "syncSourceHost" : "",
                        "syncSourceId" : -1,
                        "infoMessage" : "",
                        "electionTime" : Timestamp(1602496641, 2),
                        "electionDate" : ISODate("2020-10-12T09:57:21Z"),
                        "configVersion" : 1,
                        "self" : true,
                        "lastHeartbeatMessage" : ""
                }
        ],
        "ok" : 1,
        "operationTime" : Timestamp(1602505374, 1),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1602505374, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
}

說明:
1) "set" : "myrs" :副本集的名字
2) "myState" : 1:說明狀態正常
3) "members" :副本整合員陣列,此時只有一個: "name" : "localhost:27017" ,該成員的角色是 "stateStr" : "PRIMARY", 該節點是健康的: "health" : 1 。

1.4.8 第四步:新增副本從節點
在主節點新增從節點,將其他成員加入到副本集
語法:

rs.add(host, arbiterOnly)
Parameter Type Description
host string or document 要新增到副本集的新成員。 指定為字串或配置文件:1)如 果是一個字串,則需要指定新成員的主機名和可選的埠 號;2)如果是一個文件,請指定在members陣列中找到的副 本整合員配置文件。 您必須在成員配置文件中指定主機欄位。 有關文件配置欄位的說明,詳見下方文件:“主機成員的配置文 檔”
arbiterOnly boolean 可選的。 僅在 值為字串時適用。 如果為true,則添 加的主機是仲裁者。

示例】
將27018的副本節點新增到副本集中:

myrs:PRIMARY> rs.add("localhost:27018")
{
        "ok" : 1,
        "operationTime" : Timestamp(1602505461, 1),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1602505461, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
}

說明:
1) "ok" : 1 :說明新增成功。
檢視副本集狀態:

myrs:PRIMARY> rs.status()
{
        "set" : "myrs",
        "date" : ISODate("2020-10-12T12:24:40.502Z"),
        "myState" : 1,
        "term" : NumberLong(1),
        "syncingTo" : "",
        "syncSourceHost" : "",
        "syncSourceId" : -1,
        "heartbeatIntervalMillis" : NumberLong(2000),
        "optimes" : {
                "lastCommittedOpTime" : {
                        "ts" : Timestamp(1602505474, 1),
                        "t" : NumberLong(1)
                },
                "readConcernMajorityOpTime" : {
                        "ts" : Timestamp(1602505474, 1),
                        "t" : NumberLong(1)
                },
                "appliedOpTime" : {
                        "ts" : Timestamp(1602505474, 1),
                        "t" : NumberLong(1)
                },
                "durableOpTime" : {
                        "ts" : Timestamp(1602505474, 1),
                        "t" : NumberLong(1)
                }
        },
        "lastStableCheckpointTimestamp" : Timestamp(1602505461, 1),
        "members" : [
                {
                        "_id" : 0,
                        "name" : "localhost:27017",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "uptime" : 9193,
                        "optime" : {
                                "ts" : Timestamp(1602505474, 1),
                                "t" : NumberLong(1)
                        },
                        "optimeDate" : ISODate("2020-10-12T12:24:34Z"),
                        "syncingTo" : "",
                        "syncSourceHost" : "",
                        "syncSourceId" : -1,
                        "infoMessage" : "",
                        "electionTime" : Timestamp(1602496641, 2),
                        "electionDate" : ISODate("2020-10-12T09:57:21Z"),
                        "configVersion" : 2,
                        "self" : true,
                        "lastHeartbeatMessage" : ""
                },
                {
                        "_id" : 1,
                        "name" : "localhost:27018",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 18,
                        "optime" : {
                                "ts" : Timestamp(1602505474, 1),
                                "t" : NumberLong(1)
                        },
                        "optimeDurable" : {
                                "ts" : Timestamp(1602505474, 1),
                                "t" : NumberLong(1)
                        },
                        "optimeDate" : ISODate("2020-10-12T12:24:34Z"),
                        "optimeDurableDate" : ISODate("2020-10-12T12:24:34Z"),
                        "lastHeartbeat" : ISODate("2020-10-12T12:24:39.546Z"),
                        "lastHeartbeatRecv" : ISODate("2020-10-12T12:24:39.823Z"),
                        "pingMs" : NumberLong(0),
                        "lastHeartbeatMessage" : "",
                        "syncingTo" : "localhost:27017",
                        "syncSourceHost" : "localhost:27017",
                        "syncSourceId" : 0,
                        "infoMessage" : "",
                        "configVersion" : 2
                }
        ],
        "ok" : 1,
        "operationTime" : Timestamp(1602505474, 1),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1602505474, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
}

說明:

1) "name" : "localhost:27018" 是第二個節點的名字,其角色是 "stateStr" :"SECONDARY"

1.4.9 第五步:新增仲裁從節點

新增一個仲裁節點到副本集
語法:

rs.addArb(host)

將27019的仲裁節點新增到副本集中:

myrs:PRIMARY> rs.addArb("localhost:27019")
{
        "ok" : 1,
        "operationTime" : Timestamp(1602505564, 2),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1602505564, 2),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
}

說明:
1) "ok" : 1 :說明新增成功。
檢視副本集狀態:

myrs:PRIMARY> rs.status()
{
        "set" : "myrs",
        "date" : ISODate("2020-10-12T12:26:27.106Z"),
        "myState" : 1,
        "term" : NumberLong(1),
        "syncingTo" : "",
        "syncSourceHost" : "",
        "syncSourceId" : -1,
        "heartbeatIntervalMillis" : NumberLong(2000),
        "optimes" : {
                "lastCommittedOpTime" : {
                        "ts" : Timestamp(1602505584, 1),
                        "t" : NumberLong(1)
                },
                "readConcernMajorityOpTime" : {
                        "ts" : Timestamp(1602505584, 1),
                        "t" : NumberLong(1)
                },
                "appliedOpTime" : {
                        "ts" : Timestamp(1602505584, 1),
                        "t" : NumberLong(1)
                },
                "durableOpTime" : {
                        "ts" : Timestamp(1602505584, 1),
                        "t" : NumberLong(1)
                }
        },
        "lastStableCheckpointTimestamp" : Timestamp(1602505564, 2),
        "members" : [
                {
                        "_id" : 0,
                        "name" : "localhost:27017",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "uptime" : 9300,
                        "optime" : {
                                "ts" : Timestamp(1602505584, 1),
                                "t" : NumberLong(1)
                        },
                        "optimeDate" : ISODate("2020-10-12T12:26:24Z"),
                        "syncingTo" : "",
                        "syncSourceHost" : "",
                        "syncSourceId" : -1,
                        "infoMessage" : "",
                        "electionTime" : Timestamp(1602496641, 2),
                        "electionDate" : ISODate("2020-10-12T09:57:21Z"),
                        "configVersion" : 3,
                        "self" : true,
                        "lastHeartbeatMessage" : ""
                },
                {
                        "_id" : 1,
                        "name" : "localhost:27018",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 125,
                        "optime" : {
                                "ts" : Timestamp(1602505584, 1),
                                "t" : NumberLong(1)
                        },
                        "optimeDurable" : {
                                "ts" : Timestamp(1602505584, 1),
                                "t" : NumberLong(1)
                        },
                        "optimeDate" : ISODate("2020-10-12T12:26:24Z"),
                        "optimeDurableDate" : ISODate("2020-10-12T12:26:24Z"),
                        "lastHeartbeat" : ISODate("2020-10-12T12:26:26.804Z"),
                        "lastHeartbeatRecv" : ISODate("2020-10-12T12:26:25.818Z"),
                        "pingMs" : NumberLong(0),
                        "lastHeartbeatMessage" : "",
                        "syncingTo" : "localhost:27017",
                        "syncSourceHost" : "localhost:27017",
                        "syncSourceId" : 0,
                        "infoMessage" : "",
                        "configVersion" : 3
                },
                {
                        "_id" : 2,
                        "name" : "localhost:27019",
                        "health" : 1,
                        "state" : 7,
                        "stateStr" : "ARBITER",
                        "uptime" : 22,
                        "lastHeartbeat" : ISODate("2020-10-12T12:26:26.804Z"),
                        "lastHeartbeatRecv" : ISODate("2020-10-12T12:26:26.818Z"),
                        "pingMs" : NumberLong(0),
                        "lastHeartbeatMessage" : "",
                        "syncingTo" : "",
                        "syncSourceHost" : "",
                        "syncSourceId" : -1,
                        "infoMessage" : "",
                        "configVersion" : 3
                }
        ],
        "ok" : 1,
        "operationTime" : Timestamp(1602505584, 1),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1602505584, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
}

說明:
1) "name" : "localhost:27019" 是第二個節點的名字,其角色是 "stateStr" : "ARBITER"

1.5 副本集的資料讀寫操作

目標:測試三個不同角色的節點的資料讀寫情況。
登入主節點27017,寫入和讀取資料:

myrs:PRIMARY> use articledb
switched to db articledb
myrs:PRIMARY> db
articledb
myrs:PRIMARY> db.comment.insert({"articleid":"100000","content":"今天天氣真好,陽光明媚","userid":"1001","nickname":"Rose","createdatetime":new Date()})
WriteResult({ "nInserted" : 1 })
myrs:PRIMARY> db.comment.find()
{ "_id" : ObjectId("5f844c02d75b8f0f5671bf36"), "articleid" : "100000", "content" : "今天天氣真好,陽光明媚", "userid" : "1001", "nickname" : "Rose", "createdatetime" : ISODate("2020-10-12T12:28:50.774Z") }
myrs:PRIMARY>

登入從節點27018

[root@localhost ~]# /usr/local/mongodb/bin/mongo  --port 27018
MongoDB shell version v4.0.10
connecting to: mongodb://127.0.0.1:27018/?gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("bc619f58-4a9a-4863-99da-627459fc6f7f") }
MongoDB server version: 4.0.10
Server has startup warnings:
2020-10-12T17:54:00.894+0800 I CONTROL  [initandlisten]
2020-10-12T17:54:00.894+0800 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2020-10-12T17:54:00.894+0800 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2020-10-12T17:54:00.894+0800 I CONTROL  [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
2020-10-12T17:54:00.894+0800 I CONTROL  [initandlisten]
2020-10-12T17:54:00.895+0800 I CONTROL  [initandlisten]
2020-10-12T17:54:00.895+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2020-10-12T17:54:00.895+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2020-10-12T17:54:00.895+0800 I CONTROL  [initandlisten]
2020-10-12T17:54:00.895+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
2020-10-12T17:54:00.895+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2020-10-12T17:54:00.895+0800 I CONTROL  [initandlisten]
myrs:SECONDARY> show dbs;
2020-10-12T20:29:40.653+0800 E QUERY    [js] Error: listDatabases failed:{
        "operationTime" : Timestamp(1602505774, 1),
        "ok" : 0,
        "errmsg" : "not master and slaveOk=false",
        "code" : 13435,
        "codeName" : "NotMasterNoSlaveOk",
        "$clusterTime" : {
                "clusterTime" : Timestamp(1602505774, 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:139:1
shellHelper.show@src/mongo/shell/utils.js:882:13
shellHelper@src/mongo/shell/utils.js:766:15
@(shellhelp2):1:1

發現,不能讀取集合的資料。當前從節點只是一個備份,不是奴隸節點,無法讀取資料,寫當然更不行。
因為預設情況下,從節點是沒有讀寫許可權的,可以增加讀的許可權,但需要進行設定。
設定讀操作許可權:
說明:
設定為奴隸節點,允許在從成員上執行讀的操作
語法:

rs.slaveOk()

提示:
該命令是 db.getMongo().setSlaveOk() 的簡化命令。
【示例】
在27018上設定作為奴隸節點許可權,具備讀許可權

myrs:SECONDARY> how dbs;
2020-10-12T20:30:10.502+0800 E QUERY    [js] SyntaxError: missing ; before statement @(shell):1:4
myrs:SECONDARY> show dbs;
admin      0.000GB
articledb  0.000GB
config     0.000GB
local      0.000GB
myrs:SECONDARY> se articledb
2020-10-12T20:30:20.606+0800 E QUERY    [js] SyntaxError: missing ; before statement @(shell):1:3
myrs:SECONDARY> use articledb
switched to db articledb
myrs:SECONDARY> show collections
comment
myrs:SECONDARY> db.comment.find()
{ "_id" : ObjectId("5f844c02d75b8f0f5671bf36"), "articleid" : "100000", "content" : "今天天氣真好,陽光明媚", "userid" : "1001", "nickname" : "Rose", "createdatetime" : ISODate("2020-10-12T12:28:50.774Z") }
myrs:SECONDARY> db.comment.insert({"articleid":"100002","content":"今天天氣真好,陽光明媚","userid":"1001","nickname":"Rose","createdatetime":new Date()})
WriteCommandError({
        "operationTime" : Timestamp(1602505854, 1),
        "ok" : 0,
        "errmsg" : "not master",
        "code" : 10107,
        "codeName" : "NotMaster",
        "$clusterTime" : {
                "clusterTime" : Timestamp(1602505854, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
})
myrs:SECONDARY>

仲裁者節點,不存放任何業務資料的,可以登入檢視

[root@localhost ~]# /usr/local/mongodb/bin/mongo  --port 27019
MongoDB shell version v4.0.10
connecting to: mongodb://127.0.0.1:27019/?gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("8f6cfc15-cd7b-43d0-b13e-e2a9de81e017") }
MongoDB server version: 4.0.10
Server has startup warnings:
2020-10-12T17:56:50.460+0800 I CONTROL  [initandlisten]
2020-10-12T17:56:50.460+0800 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2020-10-12T17:56:50.460+0800 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2020-10-12T17:56:50.460+0800 I CONTROL  [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
2020-10-12T17:56:50.460+0800 I CONTROL  [initandlisten]
2020-10-12T17:56:50.461+0800 I CONTROL  [initandlisten]
2020-10-12T17:56:50.461+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2020-10-12T17:56:50.461+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2020-10-12T17:56:50.461+0800 I CONTROL  [initandlisten]
2020-10-12T17:56:50.461+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
2020-10-12T17:56:50.461+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2020-10-12T17:56:50.461+0800 I CONTROL  [initandlisten]
myrs:ARBITER> rs.slaveOk()
myrs:ARBITER> show dbs
local  0.000GB
myrs:ARBITER> use local
switched to db local
myrs:ARBITER> show collections
replset.minvalid
replset.oplogTruncateAfterPoint
startup_log
system.replset
system.rollback.id
myrs:ARBITER>

發現,只存放副本集配置等資料。

1.6 主節點的選舉原則

MongoDB在副本集中,會自動進行主節點的選舉,主節點選舉的觸發條件:
1) 主節點故障
2) 主節點網路不可達(預設心跳資訊為10秒)
3) 人工干預(rs.stepDown(600))
一旦觸發選舉,就要根據一定規則來選主節點。

選舉規則是根據票數來決定誰獲勝:
票數最高,且獲得了“大多數”成員的投票支援的節點獲勝。
“大多數”的定義為:假設複製集內投票成員數量為N,則大多數為 N/2 + 1。例如:3個投票成員,則大多數的值是2。當複製集記憶體活成員數量不足大多數時,整個複製集將無法選舉出Primary,複製集將無法提供寫服務,處於只讀狀態。
若票數相同,且都獲得了“大多數”成員的投票支援的,資料新的節點獲勝。資料的新舊是通過操作日誌oplog來對比的。
在獲得票數的時候,優先順序(priority)引數影響重大。可以通過設定優先順序(priority)來設定額外票數。優先順序即權重,取值為0-1000,相當於可額外增加
0-1000的票數,優先順序的值越大,就越可能獲得多數成員的投票(votes)數。指定較高的值可使成員更有資格成為主要成員,更低的值可使成員更不符合條件。
預設情況下,優先順序的值是1

myrs:PRIMARY> rs.conf()
{
        "_id" : "myrs",
        "version" : 3,
        "protocolVersion" : NumberLong(1),
        "writeConcernMajorityJournalDefault" : true,
        "members" : [
                {
                        "_id" : 0,
                        "host" : "localhost:27017",
                        "arbiterOnly" : false,
                        "buildIndexes" : true,
                        "hidden" : false,
                        "priority" : 1,
                        "tags" : {

                        },
                        "slaveDelay" : NumberLong(0),
                        "votes" : 1
                },
                {
                        "_id" : 1,
                        "host" : "localhost:27018",
                        "arbiterOnly" : false,
                        "buildIndexes" : true,
                        "hidden" : false,
                        "priority" : 1,
                        "tags" : {

                        },
                        "slaveDelay" : NumberLong(0),
                        "votes" : 1
                },
                {
                        "_id" : 2,
                        "host" : "localhost:27019",
                        "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("5f84288139869ebb123f25db")
        }
}
myrs:PRIMARY>

可以看出,主節點和副本節點的優先順序各為1,即,預設可以認為都已經有了一票。但選舉節點,優先順序是0,(要注意是,官方說了,選舉節點的優先順序必須是0,不能是別的值。即不具備選舉權,但具有投票權)
【瞭解】修改優先順序
比如,下面提升從節點的優先順序:

myrs:PRIMARY> cfg=rs.conf()
{
        "_id" : "myrs",
        "version" : 3,
        "protocolVersion" : NumberLong(1),
        "writeConcernMajorityJournalDefault" : true,
        "members" : [
                {
                        "_id" : 0,
                        "host" : "localhost:27017",
                        "arbiterOnly" : false,
                        "buildIndexes" : true,
                        "hidden" : false,
                        "priority" : 1,
                        "tags" : {

                        },
                        "slaveDelay" : NumberLong(0),
                        "votes" : 1
                },
                {
                        "_id" : 1,
                        "host" : "localhost:27018",
                        "arbiterOnly" : false,
                        "buildIndexes" : true,
                        "hidden" : false,
                        "priority" : 1,
                        "tags" : {

                        },
                        "slaveDelay" : NumberLong(0),
                        "votes" : 1
                },
                {
                        "_id" : 2,
                        "host" : "localhost:27019",
                        "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("5f84288139869ebb123f25db")
        }
}
myrs:PRIMARY> cfg.members[1].priority=2
2
myrs:PRIMARY> rs.reconfig(cfg)
{
        "ok" : 1,
        "operationTime" : Timestamp(1602509361, 1),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1602509361, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
}
myrs:PRIMARY>

1.7 故障測試

1.7.1 副本節點故障測試

關閉27018副本節點:
發現,主節點和仲裁節點對27018的心跳失敗。因為主節點還在,因此,沒有觸發投票選舉。
如果此時,在主節點寫入資料。

再啟動從節點,會發現,主節點寫入的資料,會自動同步給從節點。

1.7.2主節點故障測試

關閉27017節點
發現,從節點和仲裁節點對27017的心跳失敗,當失敗超過10秒,此時因為沒有主節點了,會自動發起投票。
而副本節點只有27018,因此,候選人只有一個就是27018,開始投票。

27019向27018投了一票,27018本身自帶一票,因此共兩票,超過了“大多數”
27019是仲裁節點,沒有選舉權,27018不向其投票,其票數是0.

最終結果,27018成為主節點。具備讀寫功能。
在27018寫入資料檢視。

再啟動 27017節點,發現27017變成了從節點,27018仍保持主節點。
登入27017節點,發現是從節點了,資料自動從27018同步。
從而實現了高可用。

1.7.3 仲裁節點和主節點故障

先關掉仲裁節點27019,
關掉現在的主節點27018

登入27017後,發現,27017仍然是從節點,副本集中沒有主節點了,導致此時,副本集是隻讀狀態,無法寫入。
為啥不選舉了?因為27017的票數,沒有獲得大多數,即沒有大於等於2,它只有預設的一票(優先順序是1)
如果要觸發選舉,隨便加入一個成員即可。

如果只加入 27019仲裁節點成員,則主節點一定是27017,因為沒得選了,仲裁節點不參與選舉,但參與投票。(不演示)
如果只加入 27018節點,會發起選舉。因為27017和27018都是兩票,則按照誰資料新,誰當主節點。

1.7.4 仲裁節點和從節點故障

先關掉仲裁節點27019,
關掉現在的副本節點27018
10秒後,27017主節點自動降級為副本節點。(服務降級)
副本集不可寫資料了,已經故障了。