Mongodb 高可用方案及副本集搭建
如果業務場景不需要強力的事務支援及複雜的join, 資料模型變化頻繁,資料需要落地,查詢 QPS 超過200。 那麼 Mongodb 作為資料庫非常合適。
在我們的業務中我們就選用了 mongo 來儲存賬單,選單,交易資訊等資料。隨著 mongodb 在我們的業務場景中應用的地方越來越廣,mongo 必須是高可用的。
這裡主要介紹一下 Mongodb 高可用方案以及其中的 replicaset(副本集)方案在生產上的搭建。
高可用方案
Master-Slave 主從架構
主從架構一般用於備份或者做讀寫分離。一般有一主一從設計和一主多從設計。

由兩種角色構成:
-
主(Master)
可讀可寫,當資料有修改的時候,會將oplog同步到所有連線的salve上去。
-
從(Slave)
只讀不可寫,自動從Master同步資料。
特別的,對於 Mongodb 來說,並不推薦使用 Master-Slave 架構,因為 Master-Slave 其中 Master 宕機後不能自動恢復
在主從結構中,主節點的操作記錄成為 oplog(operation log), oplog 儲存在一個系統資料庫local的集合oplog.$main中,這個集合的每個文件都代表主節點上執行的一個操作。
從伺服器會定期從主伺服器中獲取 oplog 記錄,然後在本機上執行。對於儲存 oplog 的集合,MongoDB採用的是固定集合,也就是說隨著操作過多,新的操作會覆蓋舊的操作。
ReplicaSet(副本集)
Mongodb的 ReplicaSet 即副本集方式主要有兩個目的,一個是資料冗餘做故障恢復使用,當發生硬體故障或者其它原因造成的宕機時,可以使用副本進行恢復。
另一個是做讀寫分離,讀的請求分流到副本上,減輕主(Primary)的讀壓力。

Replica Set是mongod的例項集合,它們有著同樣的資料內容。包含三類角色:
-
主節點(Primary)
接收所有的寫請求,然後把修改同步到所有Secondary。一個Replica Set只能有一個Primary節點,當Primary掛掉後,其他Secondary或者Arbiter節點會重新選舉出來一個主節點。
預設讀請求也是發到 Primary 節點處理的,需要轉發到 Secondary 需要客戶端修改一下連線配置。
-
副本節點(Secondary)
副本節點同樣使用 oplog 進行資料同步來與主節點保持同樣的資料集。當主節點掛掉的時候,副本節點參與選主。
-
仲裁者(Arbiter)
不保有資料,不參與選主,只進行選主投票。使用Arbiter可以減輕資料儲存的硬體需求,Arbiter跑起來幾乎沒什麼大的硬體資源需求,但重要的一點是,在生產環境下它和其他資料節點不要部署在同一臺機器上。
注意,一個自動failover的 ReplicaSet 節點數必須為奇數,目的是選主投票的時候要有一個大多數才能進行選主決策。
自動故障轉移

當主節點與其他節點通訊失聯的時間超過選舉超時時間(預設是10s), 副本節點會提名自己成為主節點候選者。然後完成選主,叢集則完成故障轉移。
在故障轉移過程中,寫操作失敗,副本節點仍然能正常的完成讀操作。
Sharding(分片)
當資料量比較大的時候,我們需要把資料分片執行在不同的機器中,以降低CPU、記憶體和IO的壓力,Sharding就是資料庫分片。
MongodB 分片技術類似SQL/">MySQL的水平切分和垂直切分,資料庫主要由兩種方式做 Sharding:垂直擴充套件和橫向切分。
- 垂直擴充套件的方式就是進行叢集擴充套件,新增更多的CPU,記憶體,磁碟空間等。
- 橫向切分則是通過資料分片的方式,通過叢集統一提供服務
Mongodb sharded cluster 架構圖如下

Mongodb sharded cluster中的元件包含以下三大部分:
-
shards
用來儲存資料,保證資料的高可用性和一致性。可以是一個單獨的mongod例項,也可以是一個副本集。
在生產環境下Shard一般是一個Replica Set,以防止該資料片的單點故障。
-
mongos
mongos承擔客戶端請求路由的作用。客戶端直接連線mongos,由mongos把讀寫請求路由到指定的Shard上去。
一個Sharding叢集,可以有一個mongos,也可以有多mongos以減輕客戶端請求的壓力。
-
config server
儲存叢集的元資料(metadata),包含各個Shard的路由規則。
下面這張圖是我對 Mongodb 分片架構重點的總結
ReplicaSet 搭建
這裡我們根據自己的業務場景和資料量我們選取的 mongo 高可用架構是 包含arbiter的三節點副本集 。
搭建環境是 Ubuntu, Mongodb 版本是4.0
- 配置國內的 deb 源
echo "deb [ arch=amd64,arm64 ] http://mirrors.aliyun.com/mongodb/apt/ubuntu xenial/mongodb-org/4.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.0.list
-
安裝 mongo 4.0
sudo apt-get update sudo apt-get install -y mongodb-org=4.0.0 mongodb-org-server=4.0.0 mongodb-org-shell=4.0.0 mongodb-org-mongos=4.0.0 mongodb-org-tools=4.0.0 --allow-unauthenticated
-
修改配置 /etc/mongod.conf
# mongod.conf # for documentation of all options, see: # http://docs.mongodb.org/manual/reference/configuration-options/ # Where and how to store data. storage: dbPath: /var/lib/mongodb # 重點,arbiter節點置為 false, 主副節點置為 true journal: enabled: false # engine: # mmapv1: # wiredTiger: # where to write logging data. systemLog: destination: file logAppend: true path: /var/log/mongodb/mongod.log # network interfaces net: port: 27017 bindIp: 0.0.0.0 # 重點,副本集的名字 replication: replSetName: waimai_rs #processManagement: security: authorization: enabled keyFile: /home/zaihui/keys/mongodb/mongodb-keyfile
-
啟動 mongo 例項
sudo service mongod start
如果 monod.service not found 使用
sudo systemctl unmask mongodb
-
重複上面的步驟,啟動三個例項
-
初始化副本集
進入 mongo shell
rs.initiate({_id:"waimai_rs",members:[ {_id:0,host:"172.31.41.20:27017",priority:1}, {_id:1,host:"172.31.48.196:27017",priority:0.5}, {_id:2, host:"172.31.48.227:27017",arbiterOnly:true} ]})
在這裡,我們把 priority 設定的不一樣是為了指明主節點為 priority 最高的那個。
-
檢查副本集 配置
rs.conf()
看到如下配置
{ "_id" : "waimai_rs", "version" : 1, "protocolVersion" : NumberLong(1), "writeConcernMajorityJournalDefault" : true, "members" : [ { "_id" : 0, "host" : "172.31.41.20:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, ....
-
檢視副本集狀態
rs.status()
看到如下輸出,檢查各個節點是否正常
"members" : [ { "_id" : 0, "name" : "172.31.41.20:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 1215722, "optime" : { "ts" : Timestamp(1537330095, 1), "t" : NumberLong(2) }, "optimeDurable" : { "ts" : Timestamp(1537330095, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2018-09-19T04:08:15Z"), "optimeDurableDate" : ISODate("2018-09-19T04:08:15Z"), "lastHeartbeat" : ISODate("2018-09-19T04:08:19.593Z"), "lastHeartbeatRecv" : ISODate("2018-09-19T04:08:18.644Z"), "pingMs" : NumberLong(1), "lastHeartbeatMessage" : "", "syncingTo" : "", "syncSourceHost" : "", "syncSourceId" : -1, "infoMessage" : "", "electionTime" : Timestamp(1536114379, 1), "electionDate" : ISODate("2018-09-05T02:26:19Z"), "configVersion" : 1 }, ...
-
驗證
主從伺服器資料是否同步,從伺服器沒有讀寫許可權
- 向主節點寫入資料 ok 後臺自動同步到副本節點,副本節點有資料
- 向副本節點寫入資料 false 副本節點不能寫
- 主節點讀取資料 ok
- 副本節點讀取資料 false 副本節點不能讀
- 配置副本節點可讀
- 副本節點讀取資料 ok
注意,遇到問題時要仔細檢視日誌資訊,能幫助我們快速定位問題。