MONGO學(xué)習(xí)之旅(六、副本集)

副本集

在生產(chǎn)環(huán)境中,我們不建議使用單機版的MongoDB服務(wù)器,因為:

  • 單機版的MongoDB無法保證可靠性,一旦進(jìn)程發(fā)生故障或者服務(wù)器宕機,業(yè)務(wù)將直接不可使用。
  • 一旦服務(wù)器上磁盤損壞,數(shù)據(jù)會直接丟失,而此時也沒有任何副本可以用。

對于生產(chǎn)環(huán)境的數(shù)據(jù)庫至少要保證一個或一個以上的可用副本。

Replication Set

對于MongoDB來說,數(shù)據(jù)高可用是通過副本集架構(gòu)(Replication Set)實現(xiàn)的,由一個主節(jié)點(Primary)和若干個子節(jié)點(Secondary)組成,其架構(gòu)圖如下:

image.png

客戶端通過數(shù)據(jù)庫主節(jié)點寫入數(shù)據(jù),由備份節(jié)點進(jìn)行復(fù)制同步,這樣所有的節(jié)點都會同時擁有這些業(yè)務(wù)數(shù)據(jù)的副本。

早期版本中MongoDB使用了一種Master-Slave的架構(gòu),該做法在MongoDB 3.4版本之后已廢棄。

集群選舉

Raft選舉算法

MongoDB的副本選舉是通過Raft算法來實現(xiàn)的。

Raft是一種強一致性、去中心化、高可用的分布式共識算法。所謂共識,就是多個節(jié)點對某個事情達(dá)成一致的看法,即使實在部分節(jié)點故障、網(wǎng)絡(luò)延時、網(wǎng)絡(luò)分割的情況下。

協(xié)議中的角色

  • Leader:領(lǐng)導(dǎo)者,Leader會向其他節(jié)點發(fā)送心跳,同時負(fù)責(zé)處理客戶端的讀寫操作,包括將數(shù)據(jù)同步到其他節(jié)點。
  • Follower:追隨者,響應(yīng)來自Leader和Candidate的投票請求,如果在一定時間內(nèi)沒有收到Leader的心跳,則會傳喚為Candidate。
  • Candidate:候選者,F(xiàn)ollower主動選舉轉(zhuǎn)換成Candidate,獲得投票后會成為Leader。

選舉的流程

在開始的時候,所有的節(jié)點都是Follower,此時大家都沒有辦法收到Leader的心跳。接下來,A節(jié)點先是給自己投一票,然后接著向其他節(jié)點發(fā)送投票請求,一旦A節(jié)點獲得了集群中大多數(shù)節(jié)點的投票,那么A節(jié)點將成為Leader節(jié)點,同時向其他節(jié)點廣播心跳,此時來申明自己的Leader角色。這個過程如下:

image.png

在投票中會伴隨著一些沖突或者異常出現(xiàn),為了保證達(dá)成最終的一致性,Raft協(xié)議還加入了以下的細(xì)節(jié)。

  • 在同一任期內(nèi),每個節(jié)點最多只能給一個Candidate投票(節(jié)點內(nèi)部進(jìn)行記錄),任期內(nèi)投票采用先到先得的原則。
  • 節(jié)點在收到Candidate的投票請求時,只有當(dāng)對方的任期、操作日志時間至少與自己的一樣新時,才會給它投票。
  • Candidate發(fā)起投票后,如果一直沒有得到大多數(shù)票,則會一直保持這個狀態(tài)直到超時,此后將繼續(xù)發(fā)起新一輪任期的選舉(Term自增),如果在投票期間檢測到了Leader的心跳,則會判斷Leader的任期是否至少和自己一樣新,如果是則降級為Follower,并承認(rèn)對方的Leader角色,否則不予理會。
  • 無論是Candidate節(jié)點還是Leader節(jié)點,一旦發(fā)現(xiàn)了其他節(jié)點有更新的任期(Term值),都會自動降級為Follower。

開始搭建

本次試驗中我們采用docker容器搭建

拉取Mongo鏡像

docker pull mongo:latest
image.png

我們用的MongoDB版本號是5.0

啟動MongoDB容器

啟動三個MongoDB實例,其中mongo_master 為主節(jié)點, mongo_node1、mongo_node2為從節(jié)點。
主節(jié)點負(fù)責(zé)讀寫操作,從節(jié)點可讀但不可寫。

docker run -itd --name mongo_master -p 27017:27017 mongo --replSet "re0"
docker run -itd --name mongo_node1 -p 27018:27017 mongo --replSet "re0"
docker run -itd --name mongo_node2 -p 27019:27017 mongo --replSet "re0"
image.png

查看MongoDB實例IP地址

使用docker inspect 命令查看容器信息

docker inspect mongo_master
image.png

這個地址是docker內(nèi)部虛擬網(wǎng)絡(luò)地址,容器之間可以訪問,外部是訪問不到的,在配置副本集的時候需要這個地址。
本次安裝的地址為:

172.17.0.2
172.17.0.3
172.17.0.4

172.17.0.2地址為Master地址,另外兩個為node節(jié)點。

副本集配置

進(jìn)入master容器,進(jìn)行集群配置

docker exec -it mongo_master mongo

進(jìn)入之后大概是這個樣子

image.png

初始化副本集配置

使用rs.initiate()方法初始化副本集配置

var config = {
     _id:"re0",
     members:[
         {_id:0,host:"172.17.0.2:27017"},
         {_id:1,host:"172.17.0.3:27017"},
         {_id:2,host:"172.17.0.4:27017"}
    ]
}
rs.initiate(config)

config._id: 這個是docker啟動時 replSet 設(shè)置的值。
members_id: 服務(wù)器標(biāo)識。

image.png

查看副本集配置

使用rs.conf()方法查看配置信息

rs.conf()

執(zhí)行結(jié)果如下

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

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

                        },
                        "slaveDelay" : NumberLong(0),
                        "votes" : 1
                },
                {
                        "_id" : 2,
                        "host" : "172.17.0.4: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" : 60000,
                "getLastErrorModes" : {

                },
                "getLastErrorDefaults" : {
                        "w" : 1,
                        "wtimeout" : 0
                },
                "replicaSetId" : ObjectId("6106384c55f1745e07001366")
        }
}

到這里,副本集基本上就配置好了,現(xiàn)在讓我們插入一條數(shù)據(jù)試試

測試

  • 插入數(shù)據(jù)
use order
db.user.insert({
   username:"zhouhc",
   nickname:"呢稱",
   password: "123123"
})

執(zhí)行結(jié)果如下:

re0:PRIMARY> use order
switched to db order
re0:PRIMARY> db.user.insert({
...    username:"zhouhc",
...    nickname:"呢稱",
...    password: "123123"
... })
WriteResult({ "nInserted" : 1 })
re0:PRIMARY> db.user.find()
{ "_id" : ObjectId("61064400ca2a42d1044bd372"), "username" : "zhouhc", "nickname" : "呢稱", "password" : "123123" }

連接node節(jié)點查看以下

re0:SECONDARY> exit
bye
root@6b5043311fe9:/# mongo --host 172.17.0.3:27017
MongoDB shell version v3.4.9
connecting to: mongodb://172.17.0.3:27017/
MongoDB server version: 3.4.9
Server has startup warnings:
2021-08-01T05:41:32.227+0000 I STORAGE  [initandlisten]
2021-08-01T05:41:32.227+0000 I STORAGE  [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine
2021-08-01T05:41:32.227+0000 I STORAGE  [initandlisten] **          See http://dochub.mongodb.org/core/prodnotes-filesystem
2021-08-01T05:41:32.259+0000 I CONTROL  [initandlisten]
2021-08-01T05:41:32.259+0000 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2021-08-01T05:41:32.259+0000 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2021-08-01T05:41:32.259+0000 I CONTROL  [initandlisten]
2021-08-01T05:41:32.259+0000 I CONTROL  [initandlisten]
2021-08-01T05:41:32.259+0000 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2021-08-01T05:41:32.259+0000 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2021-08-01T05:41:32.259+0000 I CONTROL  [initandlisten]
re0:SECONDARY> use order
switched to db order
re0:SECONDARY> db.user.find()
Error: error: {
        "ok" : 0,
        "errmsg" : "not master and slaveOk=false",
        "code" : 13435,
        "codeName" : "NotMasterNoSlaveOk"
}
re0:SECONDARY>

上述查詢出現(xiàn)“not master and slaveOk = false "錯誤, 這很正常,默認(rèn)情況下SECONDARY是不允許讀寫的,需要設(shè)置slaveOk
例如

re0:SECONDARY> db.getMongo().setSlaveOk()
re0:SECONDARY> db.user.find()
{ "_id" : ObjectId("61064400ca2a42d1044bd372"), "username" : "zhouhc", "nickname" : "呢稱", "password" : "123123" }

db.getMongo().setSlaveOk()只是臨時的,一旦退出在進(jìn)入Mongo也會報錯。如果需要永久性設(shè)置可以參考:MONGODB副本集的從庫永久性設(shè)置SETSLAVEOK

宕機恢復(fù)

假設(shè)業(yè)務(wù)場景Master節(jié)點宕機了,那么將從Node1和Node2中選舉一個新的PRIMARY節(jié)點

C:\Users\zhc12>docker stop mongo_master
mongo_master
C:\Users\zhc12>docker exec -it mongo_node2 mongo
MongoDB shell version v3.4.9
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.9
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:
2021-08-01T05:42:06.068+0000 I STORAGE  [initandlisten]
2021-08-01T05:42:06.068+0000 I STORAGE  [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine
2021-08-01T05:42:06.068+0000 I STORAGE  [initandlisten] **          See http://dochub.mongodb.org/core/prodnotes-filesystem
2021-08-01T05:42:06.099+0000 I CONTROL  [initandlisten]
2021-08-01T05:42:06.099+0000 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2021-08-01T05:42:06.099+0000 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2021-08-01T05:42:06.099+0000 I CONTROL  [initandlisten]
2021-08-01T05:42:06.099+0000 I CONTROL  [initandlisten]
2021-08-01T05:42:06.099+0000 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2021-08-01T05:42:06.099+0000 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2021-08-01T05:42:06.099+0000 I CONTROL  [initandlisten]
re0:PRIMARY>

這個時候NODE2就是副本集的PRIMARY節(jié)點。
這個時候重新啟動Mongo_master節(jié)點

C:\Users\zhc12>docker start mongo_master
mongo_master

進(jìn)入Mongo_master節(jié)點查看以下

C:\Users\zhc12>docker exec -it mongo_master mongo
MongoDB shell version v3.4.9
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.9
Server has startup warnings:
2021-08-01T09:15:04.910+0000 I STORAGE  [initandlisten]
2021-08-01T09:15:04.910+0000 I STORAGE  [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine
2021-08-01T09:15:04.910+0000 I STORAGE  [initandlisten] **          See http://dochub.mongodb.org/core/prodnotes-filesystem
2021-08-01T09:15:05.256+0000 I CONTROL  [initandlisten]
2021-08-01T09:15:05.256+0000 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2021-08-01T09:15:05.256+0000 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2021-08-01T09:15:05.256+0000 I CONTROL  [initandlisten]
2021-08-01T09:15:05.257+0000 I CONTROL  [initandlisten]
2021-08-01T09:15:05.257+0000 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2021-08-01T09:15:05.257+0000 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2021-08-01T09:15:05.257+0000 I CONTROL  [initandlisten]
re0:SECONDARY>

MongoBD已經(jīng)成為了SECONDARY節(jié)點。到這里Mongo副本集已經(jīng)基本布置好了,更多Mongo操作會在之后的博客中補充。

參考

MongoDB進(jìn)階與實戰(zhàn):微服務(wù)整合、性能優(yōu)化、架構(gòu)管理
MONGODB副本集的從庫永久性設(shè)置SETSLAVEOK

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容