MongoDB復制集原理詳解
復制集簡介
Mongodb復制集由一組Mongod實例(進程)組成,包含一個Primary節(jié)點和多個Secondary節(jié)點,Mongodb Driver(客戶端)的所有數(shù)據(jù)都寫入Primary,Secondary從Primary同步寫入的數(shù)據(jù),以保持復制集內(nèi)所有成員存儲相同的數(shù)據(jù)集,提供數(shù)據(jù)的高可用。
下圖(圖片源于Mongodb官方文檔)是一個典型的Mongdb復制集,包含一個Primary節(jié)點和2個Secondary節(jié)點。

Primary選舉
復制集通過replSetInitiate命令(或mongo shell的rs.initiate())進行初始化,初始化后各個成員間開始發(fā)送心跳消息,并發(fā)起Priamry選舉操作,獲得『大多數(shù)』成員投票支持的節(jié)點,會成為Primary,其余節(jié)點成為Secondary。
初始化復制集
config = {
_id : "my_replica_set",
members : [
{_id : 0, host : "rs1.example.net:27017"},
{_id : 1, host : "rs2.example.net:27017"},
{_id : 2, host : "rs3.example.net:27017"},
]
}
rs.initiate(config)
『大多數(shù)』的定義
假設(shè)復制集內(nèi)投票成員(后續(xù)介紹)數(shù)量為N,則大多數(shù)為 N/2 + 1,當復制集內(nèi)存活成員數(shù)量不足大多數(shù)時,整個復制集將無法選舉出Primary,復制集將無法提供寫服務(wù),處于只讀狀態(tài)。
| 投票成員數(shù) | 大多數(shù) | 容忍失效數(shù) |
|---|---|---|
| 1 | 1 | 0 |
| 2 | 2 | 0 |
| 3 | 2 | 1 |
| 4 | 3 | 1 |
| 5 | 3 | 2 |
| 6 | 4 | 2 |
| 7 | 4 | 3 |
通常建議將復制集成員數(shù)量設(shè)置為奇數(shù),從上表可以看出3個節(jié)點和4個節(jié)點的復制集都只能容忍1個節(jié)點失效,從『服務(wù)可用性』的角度看,其效果是一樣的。(但無疑4個節(jié)點能提供更可靠的數(shù)據(jù)存儲)
特殊的Secondary
正常情況下,復制集的Seconary會參與Primary選舉(自身也可能會被選為Primary),并從Primary同步最新寫入的數(shù)據(jù),以保證與Primary存儲相同的數(shù)據(jù)。
Secondary可以提供讀服務(wù),增加Secondary節(jié)點可以提供復制集的讀服務(wù)能力,同時提升復制集的可用性。另外,Mongodb支持對復制集的Secondary節(jié)點進行靈活的配置,以適應多種場景的需求。
Arbiter
Arbiter節(jié)點只參與投票,不能被選為Primary,并且不從Primary同步數(shù)據(jù)。
比如你部署了一個2個節(jié)點的復制集,1個Primary,1個Secondary,任意節(jié)點宕機,復制集將不能提供服務(wù)了(無法選出Primary),這時可以給復制集添加一個Arbiter節(jié)點,即使有節(jié)點宕機,仍能選出Primary。
Arbiter本身不存儲數(shù)據(jù),是非常輕量級的服務(wù),當復制集成員為偶數(shù)時,最好加入一個Arbiter節(jié)點,以提升復制集可用性。
Priority0
Priority0節(jié)點的選舉優(yōu)先級為0,不會被選舉為Primary
比如你跨機房A、B部署了一個復制集,并且想指定Primary必須在A機房,這時可以將B機房的復制集成員Priority設(shè)置為0,這樣Primary就一定會是A機房的成員。(注意:如果這樣部署,最好將『大多數(shù)』節(jié)點部署在A機房,否則網(wǎng)絡(luò)分區(qū)時可能無法選出Primary)
Vote0
Mongodb 3.0里,復制集成員最多50個,參與Primary選舉投票的成員最多7個,其他成員(Vote0)的vote屬性必須設(shè)置為0,即不參與投票。
Hidden
Hidden節(jié)點不能被選為主(Priority為0),并且對Driver不可見。
因Hidden節(jié)點不會接受Driver的請求,可使用Hidden節(jié)點做一些數(shù)據(jù)備份、離線計算的任務(wù),不會影響復制集的服務(wù)。
Delayed
Delayed節(jié)點必須是Hidden節(jié)點,并且其數(shù)據(jù)落后與Primary一段時間(可配置,比如1個小時)。
因Delayed節(jié)點的數(shù)據(jù)比Primary落后一段時間,當錯誤或者無效的數(shù)據(jù)寫入Primary時,可通過Delayed節(jié)點的數(shù)據(jù)來恢復到之前的時間點。
數(shù)據(jù)同步
Primary與Secondary之間通過oplog來同步數(shù)據(jù),Primary上的寫操作完成后,會向特殊的local.oplog.rs特殊集合寫入一條oplog,Secondary不斷的從Primary取新的oplog并應用。
因oplog的數(shù)據(jù)會不斷增加,local.oplog.rs被設(shè)置成為一個capped集合,當容量達到配置上限時,會將最舊的數(shù)據(jù)刪除掉。另外考慮到oplog在Secondary上可能重復應用,oplog必須具有冪等性,即重復應用也會得到相同的結(jié)果。
如下oplog的格式,包含ts、h、op、ns、o等字段
{
"ts" : Timestamp(1446011584, 2),
"h" : NumberLong("1687359108795812092"),
"v" : 2,
"op" : "i",
"ns" : "test.nosql",
"o" : { "_id" : ObjectId("563062c0b085733f34ab4129"), "name" : "mongodb", "score" : "100" }
}
- ts: 操作時間,當前timestamp + 計數(shù)器,計數(shù)器每秒都被重置
- h:操作的全局唯一標識
- v:oplog版本信息
- op:操作類型
- i:插入操作
- u:更新操作
- d:刪除操作
- c:執(zhí)行命令(如createDatabase,dropDatabase)
- n:空操作,特殊用途
- ns:操作針對的集合
- o:操作內(nèi)容,如果是更新操作
- o2:操作查詢條件,僅update操作包含該字段
Secondary初次同步數(shù)據(jù)時,會先進行init sync,從Primary(或其他數(shù)據(jù)更新的Secondary)同步全量數(shù)據(jù),然后不斷通過tailable cursor從Primary的local.oplog.rs集合里查詢最新的oplog并應用到自身。
init sync過程包含如下步驟
T1時間,從Primary同步所有數(shù)據(jù)庫的數(shù)據(jù)(local除外),通過listDatabases + listCollections + cloneCollection敏命令組合完成,假設(shè)T2時間完成所有操作。
從Primary應用[T1-T2]時間段內(nèi)的所有oplog,可能部分操作已經(jīng)包含在步驟1,但由于oplog的冪等性,可重復應用。
根據(jù)Primary各集合的index設(shè)置,在Secondary上為相應集合創(chuàng)建index。(每個集合_id的index已在步驟1中完成)。
oplog集合的大小應根據(jù)DB規(guī)模及應用寫入需求合理配置,配置得太大,會造成存儲空間的浪費;配置得太小,可能造成Secondary的init sync一直無法成功。比如在步驟1里由于DB數(shù)據(jù)太多、并且oplog配置太小,導致oplog不足以存儲[T1, T2]時間內(nèi)的所有oplog,這就Secondary無法從Primary上同步完整的數(shù)據(jù)集。
修改復制集配置
當需要修改復制集時,比如增加成員、刪除成員、或者修改成員配置(如priorty、vote、hidden、delayed等屬性),可通過replSetReconfig命令(rs.reconfig())對復制集進行重新配置。
比如將復制集的第2個成員Priority設(shè)置為2,可執(zhí)行如下命令
cfg = rs.conf(); cfg.members[1].priority = 2; rs.reconfig(cfg);
細說Primary選舉
Primary選舉除了在復制集初始化時發(fā)生,還有如下場景
- 復制集被reconfig
- Secondary節(jié)點檢測到Primary宕機時,會觸發(fā)新Primary的選舉
- 當有Primary節(jié)點主動stepDown(主動降級為Secondary)時,也會觸發(fā)新的Primary選舉
Primary的選舉受節(jié)點間心跳、優(yōu)先級、最新的oplog時間等多種因素影響。
節(jié)點間心跳
復制集成員間默認每2s會發(fā)送一次心跳信息,如果10s未收到某個節(jié)點的心跳,則認為該節(jié)點已宕機;如果宕機的節(jié)點為Primary,Secondary(前提是可被選為Primary)會發(fā)起新的Primary選舉。
節(jié)點優(yōu)先級
- 每個節(jié)點都傾向于投票給優(yōu)先級最高的節(jié)點
- 優(yōu)先級為0的節(jié)點不會主動發(fā)起Primary選舉
- 當Primary發(fā)現(xiàn)有優(yōu)先級更高Secondary,并且該Secondary的數(shù)據(jù)落后在10s內(nèi),則Primary會主動降級,讓優(yōu)先級更高的Secondary有成為Primary的機會。
Optime
擁有最新optime(最近一條oplog的時間戳)的節(jié)點才能被選為主。
網(wǎng)絡(luò)分區(qū)
只有更大多數(shù)投票節(jié)點間保持網(wǎng)絡(luò)連通,才有機會被選Primary;如果Primary與大多數(shù)的節(jié)點斷開連接,Primary會主動降級為Secondary。當發(fā)生網(wǎng)絡(luò)分區(qū)時,可能在短時間內(nèi)出現(xiàn)多個Primary,故Driver在寫入時,最好設(shè)置『大多數(shù)成功』的策略,這樣即使出現(xiàn)多個Primary,也只有一個Primary能成功寫入大多數(shù)。
復制集的讀寫設(shè)置
Read Preference
默認情況下,復制集的所有讀請求都發(fā)到Primary,Driver可通過設(shè)置Read Preference來將讀請求路由到其他的節(jié)點。
- primary: 默認規(guī)則,所有讀請求發(fā)到Primary
- primaryPreferred: Primary優(yōu)先,如果Primary不可達,請求Secondary
- secondary: 所有的讀請求都發(fā)到secondary
- secondaryPreferred:Secondary優(yōu)先,當所有Secondary不可達時,請求Primary
- nearest:讀請求發(fā)送到最近的可達節(jié)點上(通過ping探測得出最近的節(jié)點)
Write Concern
默認情況下,Primary完成寫操作即返回,Driver可通過設(shè)置[Write Concern(https://docs.mongodb.org/manual/core/write-concern/)來設(shè)置寫成功的規(guī)則。
如下的write concern規(guī)則設(shè)置寫必須在大多數(shù)節(jié)點上成功,超時時間為5s。
db.products.insert(
{ item: "envelopes", qty : 100, type: "Clasp" },
{ writeConcern: { w: majority, wtimeout: 5000 } }
)
上面的設(shè)置方式是針對單個請求的,也可以修改副本集默認的write concern,這樣就不用每個請求單獨設(shè)置。
cfg = rs.conf()
cfg.settings = {}
cfg.settings.getLastErrorDefaults = { w: "majority", wtimeout: 5000 }
rs.reconfig(cfg)
異常處理(rollback)
當Primary宕機時,如果有數(shù)據(jù)未同步到Secondary,當Primary重新加入時,如果新的Primary上已經(jīng)發(fā)生了寫操作,則舊Primary需要回滾部分操作,以保證數(shù)據(jù)集與新的Primary一致。
舊Primary將回滾的數(shù)據(jù)寫到單獨的rollback目錄下,數(shù)據(jù)庫管理員可根據(jù)需要使用mongorestore進行恢復。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
分布式文檔存儲數(shù)據(jù)庫之MongoDB分片集群的問題
這篇文章主要介紹了分布式文檔存儲數(shù)據(jù)庫之MongoDB分片集群的問題,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11
MongoDB最大連接數(shù)設(shè)置失效的異常分析過程與解決方法
mongodb最大連接數(shù)是20000。所以業(yè)界流傳一段話,千萬級以下的用mysql、千萬級以上的用mongodb,億級以上的用hadoop。下面這篇文章主要給大家介紹了關(guān)于MongoDB最大連接數(shù)設(shè)置失效的異常分析過程,需要的朋友可以參考下2018-09-09
MongoDB aggregate 運用篇個人總結(jié)
最近一直在用mongodb,有時候會需要用到統(tǒng)計,在網(wǎng)上查了一些資料,最適合用的就是用aggregate,以下介紹一下自己運用的心得2016-11-11
mongodb eval 執(zhí)行服務(wù)器端腳本
在MongoDB的服務(wù)器端可以通過db.eval函數(shù)來執(zhí)行javascript腳本,如我們可以定義一個javascript函數(shù),然后通過db.eval在服務(wù)器端來運行!我們前面其實也接觸過在服務(wù)器段運行一個預定義的javascript腳本的情況,如在$where查詢,執(zhí)行mapreduce任務(wù)等。2015-05-05

