- 一、什麼是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個投票節點,其餘節點必須是沒有投票權的節點。
副本集的最小推薦配置是三個節點:
- 一個主節點和兩個從節點。
- 一個主節點、一個從節點和仲裁節點。
三、副本集的成員
副本集有兩種型別三種角色
-
兩種型別
-
主節點(Primary)型別:資料操作的主要連線點,可讀寫
-
次要(輔助、從)節點(Secondaries)型別:資料冗餘備份節點,可以讀或選舉
-
-
三種角色
-
主要成員(Primary):主要接收所有寫操作。就是主節點。
-
副本成員(Replicate):從主節點透過複製操作以維護相同的資料集,即備份資料,不可寫操作,但可以讀操作(但需要配置)。是預設的一種從節點型別
-
仲裁者(Arbiter):不保留任何資料的副本,只具有投票選舉作用,當然也可以將仲裁伺服器維護為副本集的一部分,即副本成員同時也可以是仲裁者。也是一種從節點型別。
-
-
建議
-
如果你的副本+主節點的個數是偶數,建議加一個仲裁者,形成奇數,容易滿足大多數的投票。
-
如果你的副本+主節點的個數是奇數,可以不加仲裁者。
-
四、部署副本集
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>
-
"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、所有節點故障
-
整個叢集不可用,既不能執行讀也不能執行寫操作。
-
這種情況需要手動干預,逐一排查並恢復各個節點,確保至少一個主節點和多數節點(包括仲裁節點)線上,以恢復叢集服務。
-
此時:影響正常使用,需要處理