搭建MongoDB副本集

misakivv發表於2024-04-29

目錄
  • 一、什麼是MongoDB的副本集
  • 二、副本集的架構
  • 三、副本集的成員
  • 四、部署副本集
    • 1、節點劃分
    • 2、安裝MongoDB
      • 2.1、下載解壓安裝包
    • 3、建立主節點
      • 3.1、建立儲存資料和日誌的目錄
      • 3.2、新建配置檔案
      • 3.3、啟動節點服務
    • 4、建立副本節點
      • 4.1、建立儲存資料和日誌的目錄
      • 4.2、新建配置檔案
      • 4.3、啟動節點服務
    • 5、建立仲裁節點
      • 5.1、建立儲存資料和日誌的目錄
      • 5.2、建立配置檔案
      • 5.3、啟動節點服務
    • 6、新增環境變數
    • 7、初始化副本集
      • 7.1、客戶端連線主節點
      • 7.2、初始化副本集
    • 8、檢視副本集配置資訊
    • 9、新增副本節點和仲裁節點
    • 10、再次檢視副本集配置資訊
    • 11、設定副本節點可讀
  • 五、測試副本集的資料讀寫操作
    • 1、主節點測試
    • 2、副本節點測試
      • 2.1、設定副本節點只能作為備份不能讀取
    • 3、仲裁節點不存放任何資料
  • 六、主節點的選舉原則
    • 1、主節點選舉觸發條件
    • 2、選舉規則
  • 七、叢集故障分析
    • 1、主節點故障
    • 2、副本節點故障
    • 3、仲裁節點故障
    • 4、主節點和仲裁節點故障
    • 5、從節點和仲裁節點故障
    • 6、主節點和從節點故障
    • 7、所有節點故障

一、什麼是MongoDB的副本集

MongoDB的副本集(Replica Set)是一種提供資料冗餘和高可用性的架構。它是由多個維護相同資料集的mongod程序(即資料庫例項)組成的一個集合,這些例項分佈在不同的伺服器上。

副本集的設計目標是為了確保在單個伺服器發生故障時,資料庫服務依然可以繼續運作,從而提高了系統的可靠性和容錯能力。

二、副本集的架構

  • 一個副本集最多有50個節點。一個副本集最多有7個投票節點,其餘節點必須是沒有投票權的節點。

  • 副本集的最小推薦配置是三個節點:

    • 一個主節點和兩個從節點。
    • 一個主節點、一個從節點和仲裁節點。

2270526-20220923193134773-2055031891

2270526-20220923185546781-845092680

三、副本集的成員

副本集有兩種型別三種角色

  • 兩種型別

    • 主節點(Primary)型別:資料操作的主要連線點,可讀寫

    • 次要(輔助、從)節點(Secondaries)型別:資料冗餘備份節點,可以讀或選舉

  • 三種角色

    • 主要成員(Primary):主要接收所有寫操作。就是主節點。

    • 副本成員(Replicate):從主節點透過複製操作以維護相同的資料集,即備份資料,不可寫操作,但可以讀操作(但需要配置)。是預設的一種從節點型別

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

  • 建議

    • 如果你的副本+主節點的個數是偶數,建議加一個仲裁者,形成奇數,容易滿足大多數的投票。

    • 如果你的副本+主節點的個數是奇數,可以不加仲裁者。

四、部署副本集

u=3763007939,3474838561&fm=253&fmt=auto&app=138&f=PNG

1、節點劃分

一臺機器上部署三個mongodb節點

角色
27017 主節點
27018 副本節點
27019 仲裁節點

2、安裝MongoDB

2.1、下載解壓安裝包

wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.4.6.tgz
tar xzvf mongodb-linux-x86_64-rhel70-4.4.6.tgz -C /usr/local
cd /usr/local/
ln -s /usr/local/mongodb-linux-x86_64-rhel70-4.4.6 /usr/local/mongodb

3、建立主節點

3.1、建立儲存資料和日誌的目錄

mkdir -p /usr/local/mongodb/replica_sets/myrs_27017/log
mkdir -p /usr/local/mongodb/replica_sets/myrs_27017/data/db

3.2、新建配置檔案

vim /usr/local/mongodb/replica_sets/myrs_27017/mongod.conf

systemLog:
    destination: file
    path: "/usr/local/mongodb/replica_sets/myrs_27017/log/mongod.log"
    logAppend: true
storage:
    dbPath: "/usr/local/mongodb/replica_sets/myrs_27017/data/db"
    journal:
    # 啟用永續性日誌
        enabled: true
processManagement:
    fork: true
    pidFilePath: "/usr/local/mongodb/replica_sets/myrs_27017/log/mongod.pid"
net:
    bindIp: localhost,192.168.112.40
    port: 27017
replication:
    replSetName: myrs

3.3、啟動節點服務

[root@localhost local]# /usr/local/mongodb/bin/mongod -f /usr/local/mongodb/replica_sets/myrs_27017/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 8177
child process started successfully, parent exiting

4、建立副本節點

4.1、建立儲存資料和日誌的目錄

mkdir -p /usr/local/mongodb/replica_sets/myrs_27018/log
mkdir -p /usr/local/mongodb/replica_sets/myrs_27018/data/db

4.2、新建配置檔案

vim /usr/local/mongodb/replica_sets/myrs_27018/mongod.conf

systemLog:
    destination: file
    path: "/usr/local/mongodb/replica_sets/myrs_27018/log/mongod.log"
    logAppend: true
storage:
    dbPath: "/usr/local/mongodb/replica_sets/myrs_27018/data/db"
    journal:
        enabled: true
processManagement:
    fork: true
    pidFilePath: "/usr/local/mongodb/replica_sets/myrs_27018/log/mongod.pid"
net:
    bindIp:  localhost,192.168.112.40
    port: 27018
replication:
    replSetName: myrs

4.3、啟動節點服務

[root@localhost ~]# /usr/local/mongodb/bin/mongod -f /usr/local/mongodb/replica_sets/myrs_27018/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 18263
child process started successfully, parent exiting

5、建立仲裁節點

5.1、建立儲存資料和日誌的目錄

mkdir -p /usr/local/mongodb/replica_sets/myrs_27019/log
mkdir -p /usr/local/mongodb/replica_sets/myrs_27019/data/db

5.2、建立配置檔案

vim /usr/local/mongodb/replica_sets/myrs_27019/mongod.conf

systemLog:
    destination: file
    path: "/usr/local/mongodb/replica_sets/myrs_27019/log/mongod.log"
    logAppend: true
storage:
    dbPath: "/usr/local/mongodb/replica_sets/myrs_27019/data/db"
    journal:
    # 啟用永續性日誌
        enabled: true
processManagement:
    fork: true
    pidFilePath: "/usr/local/mongodb/replica_sets/myrs_27019/log/mongod.pid"
net:
    bindIp:  localhost,192.168.112.40
    port: 27019
replication:
    replSetName: myrs

5.3、啟動節點服務

[root@localhost ~]# /usr/local/mongodb/bin/mongod -f /usr/local/mongodb/replica_sets/myrs_27019/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 18314
child process started successfully, parent exiting

6、新增環境變數

vim /etc/profile

export PATH=/usr/local/mongodb/bin/:$PATH

source /etc/profile
[root@localhost ~]# which mongo
/usr/local/mongodb/bin/mongo

7、初始化副本集

7.1、客戶端連線主節點

mongo --host 192.168.112.40 --port=27017

7.2、初始化副本集

當前並未選舉主節點(primary),並且操作沒有設定為允許從輔助節點(secondary)上讀取。

所以很多命令無法使用,必須初始化副本集

> show dbs
uncaught exception: Error: listDatabases failed:{
        "topologyVersion" : {
                "processId" : ObjectId("662f9e53d74e84faba8865bc"),
                "counter" : NumberLong(0)
        },
        "ok" : 0,
        "errmsg" : "not master and slaveOk=false",
        "code" : 13435,
        "codeName" : "NotPrimaryNoSecondaryOk"
} :
_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

初始化副本集

> rs.initiate()
{
        "info2" : "no configuration specified. Using a default configuration for the set",
        "me" : "192.168.112.40:27017",
        "ok" : 1
}
myrs:SECONDARY>

image-20240429215626296

  • "ok"的值為1,說明建立成功。

  • 命令列提示符發生變化,變成了一個從節點角色,此時預設不能讀寫。

  • 稍等片刻,發現副本集只有自己一個,變成主節點。

8、檢視副本集配置資訊

myrs:PRIMARY> rs.conf()
{
        "_id" : "myrs",
        "version" : 1,
        "term" : 1,
        "protocolVersion" : NumberLong(1),
        "writeConcernMajorityJournalDefault" : true,
        "members" : [
                {
                        "_id" : 0,
                        "host" : "192.168.112.40: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("662fa69cd74e84faba8865e0")
        }
}
  • "_id" : "myrs" :副本集的配置資料儲存的主鍵值,預設就是副本集的名字

  • "members" :副本整合員陣列,此時只有一個:"host" : "192.168.112.40:27017"

  • 該成員不是仲裁節點: "arbiterOnly" : false

  • 優先順序(權重值): "priority" : 1

  • "settings" :副本集的引數配置。

9、新增副本節點和仲裁節點

新增副本節點

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

新增仲裁節點

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

10、再次檢視副本集配置資訊

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

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

                        },
                        "slaveDelay" : NumberLong(0),
                        "votes" : 1
                },
                {
                        "_id" : 2,
                        "host" : "192.168.112.40: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("662fa69cd74e84faba8865e0")
        }
}
  • 成員資訊:
    • 成員1(_id: 0)和成員2(_id: 1)都是資料承載節點,它們位於同一臺機器的不同埠(27017 和 27018)。兩者都有相同的優先順序("priority" : 1),意味著它們都可以成為主節點。它們都沒有延遲複製("slaveDelay" : NumberLong(0)),並且都參與選舉投票("votes" : 1)。
    • 成員3(_id: 2)是一個仲裁者(arbiterOnly: true),位於埠27019。仲裁者的角色是在選舉新主節點時起到決定性的一票作用,但它不儲存實際資料,也沒有優先順序,不參與資料複製。

11、設定副本節點可讀

rs.slaveOk()

五、測試副本集的資料讀寫操作

1、主節點測試

[root@localhost ~]# mongo --port 27017

myrs:PRIMARY> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
myrs:PRIMARY> use test1
switched to db test1
myrs:PRIMARY> db.q1.insert({"id":"1000","content":"學習部署副本集","userid":"001","name":"zhangsan","createdatatime":new Date(),"state":null})
WriteResult({ "nInserted" : 1 })
myrs:PRIMARY> db.q1.find().pretty()
{
        "_id" : ObjectId("662fb0d727a6cea3c847b26e"),
        "id" : "1000",
        "content" : "學習部署副本集",
        "userid" : "001",
        "name" : "zhangsan",
        "createdatatime" : ISODate("2024-04-29T14:38:15.243Z"),
        "state" : null
}

2、副本節點測試

[root@localhost ~]# mongo --port 27018

myrs:SECONDARY> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
test1   0.000GB
myrs:SECONDARY> use test1
switched to db test1
myrs:SECONDARY> show tables;
q1
myrs:SECONDARY> db.q1.find().pretty()
{
        "_id" : ObjectId("662fb0d727a6cea3c847b26e"),
        "id" : "1000",
        "content" : "學習部署副本集",
        "userid" : "001",
        "name" : "zhangsan",
        "createdatatime" : ISODate("2024-04-29T14:38:15.243Z"),
        "state" : null
}
myrs:SECONDARY> db.q1.insert({"id":"1001","content":"學習部署副本集","userid":"002","name":"lisi","createdatatime":new Date(),"state":null})
WriteCommandError({
        "topologyVersion" : {
                "processId" : ObjectId("662fa148dfb1efa1f75795c3"),
                "counter" : NumberLong(4)
        },
        "operationTime" : Timestamp(1714402027, 1),
        "ok" : 0,
        "errmsg" : "not master",
        "code" : 10107,
        "codeName" : "NotWritablePrimary",
        "$clusterTime" : {
                "clusterTime" : Timestamp(1714402027, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
})
  • 無法寫入資料

    • "errmsg" : "not master"

    • "codeName" : "NotWritablePrimary"

2.1、設定副本節點只能作為備份不能讀取

#設定從節點有讀取許可權
rs.slaveOk()
# 取消從節點的資料讀取許可權
rs.slaveOk(false)

3、仲裁節點不存放任何資料

[root@localhost ~]# mongo --port 27019

myrs:ARBITER> show dbs
uncaught exception: Error: listDatabases failed:{
        "topologyVersion" : {
                "processId" : ObjectId("662fa25360629ad0a0f05cc1"),
                "counter" : NumberLong(1)
        },
        "ok" : 0,
        "errmsg" : "not master and slaveOk=false",
        "code" : 13435,
        "codeName" : "NotPrimaryNoSecondaryOk"
} :
_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
myrs:ARBITER> rs.slaveOk()
WARNING: slaveOk() is deprecated and may be removed in the next major release. Please use secondaryOk() instead.
myrs:ARBITER> show dbs
uncaught exception: Error: listDatabases failed:{
        "topologyVersion" : {
                "processId" : ObjectId("662fa25360629ad0a0f05cc1"),
                "counter" : NumberLong(1)
        },
        "ok" : 0,
        "errmsg" : "node is not in primary or recovering state",
        "code" : 13436,
        "codeName" : "NotPrimaryOrSecondary"
} :
_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

"errmsg" : "node is not in primary or recovering state"

仲裁節點(Arbiter)在MongoDB副本集中僅用於選舉過程中的投票,並不儲存資料

六、主節點的選舉原則

1、主節點選舉觸發條件

  • MongoDB在副本集中,會自動進行主節點的選舉,主節點選舉的觸發條件

    • 主節點故障

    • 主節點網路不可達(預設心跳資訊為10秒)

    • 人工干預(rs.stepDown(600))primary直接降級在600s內不會把自己選為primary

  • 一旦觸發選舉,就要根據一定規則來選擇主節點。

2、選舉規則

  • 選舉規則是根據票數來決定誰獲勝

    • 票數最高,且獲得了“大多數”成員的投票支援的節點獲勝。

    • "大多數"的定義為:假設複製集內投票成員時N,則大多數為N/2+1。例如:3個投票成員,則大多數的值是2。當複製集記憶體活成員數量不足大多數時,整個複製集將無法選舉primary,複製集將無法提供寫服務,處於只讀狀態。

    • 若票數相同,且都獲得了“大多數”成員的投票支援的,資料新的節點獲勝。

    • 資料的新舊是透過操作日誌oplog來對比的。

  • 在獲得票數的時候,優先順序(priority)引數影響重大。

  • 可以透過設定優先順序(priority)來設定額外票數。優先順序即權重,取值為0-1000,相當於增加0-1000的票數,優先順序的值越大,就越可能獲得多數成員的投票(votes)數。指定較高的值可使成員更有資格成員主要成員,更低的值可使成員更不符合條件。

  • 預設情況下,優先順序的值是1

七、叢集故障分析

1、主節點故障

  • 從節點和仲裁節點對主節點的心跳失敗,當失敗超過10秒,此時因為沒有主節點了,會自動發起投票。
    • 而副本節點只有一臺,因此,候選人只有一個就是副本節點,開始投票。
    • 仲裁節點向副本節點投了一票,副本節點本身自帶一票,因此共兩票,超過了"大多數"。
    • 27019是仲裁節點,沒有選舉權,27018不向其投票,其票數是0。
    • 最終結果,27018成為主節點。具備讀寫功能。
  • 再啟動 27017主節點,發現27017變成了從節點,27018仍保持主節點。
  • 登入27017節點,發現是從節點了,資料自動從27018同步。
  • 此時:不影響正常使用

2、副本節點故障

  • 主節點和仲裁節點對副本節點的心跳失敗。因為主節點還在,因此,沒有觸發投票選舉。
    如果此時,在主節點寫入資料。再啟動從節點,會發現,主節點寫入的資料,會自動同步給從節點。
  • 此時:不影響正常使用

3、仲裁節點故障

  • 主節點和副本節點對仲裁節點的心跳失敗。因為主節點還在,因此,沒有觸發投票選舉。
  • 此時:不影響正常使用

4、主節點和仲裁節點故障

副本集中沒有主節點了,導致此時,副本集是隻讀狀態,無法寫入。

因為27017的票數,沒有獲得大多數,即沒有大於等於2,它只有預設的一票(優先順序是1)
如果要觸發選舉,隨便加入一個成員即可。

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

此時:影響正常使用,需要處理

5、從節點和仲裁節點故障

10秒後,27017主節點自動降級為副本節點。(服務降級)
副本集不可寫資料了,已經故障了。

此時:影響正常使用,需要處理

6、主節點和從節點故障

叢集將處於不完全狀態,無法執行寫操作,因為剩餘的副本節點不足以立即選出新的主節點(假設只剩一個副本節點和仲裁節點)。直到至少有一個額外的副本節點線上並同步,以便選舉出新的主節點

此時:影響正常使用,需要處理

7、所有節點故障

  • 整個叢集不可用,既不能執行讀也不能執行寫操作。

  • 這種情況需要手動干預,逐一排查並恢復各個節點,確保至少一個主節點和多數節點(包括仲裁節點)線上,以恢復叢集服務。

  • 此時:影響正常使用,需要處理

相關文章