Raft与MongoDB复制集协议比较
在一文搞懂raft算法一文中,從raft論文出發(fā),詳細介紹了raft的工作流程以及對特殊情況的處理。但算法、協(xié)議這種偏抽象的東西,僅僅看論文還是比較難以掌握的,需要看看在工業(yè)界的具體實現(xiàn)。本文關(guān)注MongoDB是如何在復(fù)制集中使用raft協(xié)議的,對raft協(xié)議做了哪些擴展。
閱讀本文,需要對MongoDB復(fù)制集replication有一定認識,特別是replicat set protocol version。
在帶著問題學習分布式系統(tǒng)之中心化復(fù)制集一文中,介紹了中心化副本控制協(xié)議。在raft(mongodb pv1)中,也是通過先選舉出leader(primary),然后通過leader(primary)管理整個復(fù)制集。
在3.2以及之后的版本中,mongodb默認使用protocol version 1。從官方的一些資料、視頻可以看到,這個是一個raft-like的協(xié)議。本文主要從leader-election和log replication這兩個角度來對比mongodb rs pv1與raft,并試圖分析差異的原因。
需要注意的是,本文所有對MongoDB復(fù)制集的分析都是基于MongoDb3.4
本文地址:https://www.cnblogs.com/xybaby/p/10165564.html
leader election
首先對raft協(xié)議中l(wèi)eader election做幾點總結(jié):
選舉、投票資格
為了簡化協(xié)議,使得raft更容易理解,raft中所有節(jié)點都能發(fā)起選舉、參與投票。但在MongoDB中,有更為豐富的選舉控制策略,我們從Replica Set Configuration就能看出來,replica set中的節(jié)點可以配置以下屬性
members: [{_id: <int>,host: <string>,arbiterOnly: <boolean>,buildIndexes: <boolean>,hidden: <boolean>,priority: <number>,tags: <document>,slaveDelay: <int>,votes: <number>},...],- arbiterOnly: Arbiter上沒有用戶數(shù)據(jù),只能投票,不能發(fā)起選舉,其作用在于用盡量少的資源使得復(fù)制集中節(jié)點數(shù)目為奇數(shù)。
- hidden:雖然有數(shù)據(jù),但對客戶端不可見,可以用來做備份等其他用途。hidden的priority一定是0,因此不可以發(fā)起選舉,但是可以投票
- priority:A number that indicates the relative eligibility of a member to become a primary. priority為0時是不能發(fā)起選舉的。
- votes:是否可以參與投票,mongodb復(fù)制集中最多可以有50個節(jié)點,但最多只有7個可以投票,其作用在于降低復(fù)雜度。
priority
這里再單獨強調(diào)一下priority,mongodb中
Changing the balance of priority in a replica set will trigger one or more elections. If a lower priority secondary is elected over a higher priority secondary, replica set members will continue to call elections until the highest priority available member becomes primary.
通過rs.reconfig()修改節(jié)點的優(yōu)先級的時候,會觸發(fā)重新選舉。整個復(fù)制集會不斷發(fā)起選舉,直到最高優(yōu)先級的節(jié)點成為primary。當然,在選舉-投票的過程中,還是必須滿足候選者數(shù)據(jù)足夠新的約束。
priority很有用,比如在multi datacenter deploy的情況下,我們可能根據(jù)用戶的分布情況來確定primary在哪個datacenter。
heartbeat
raft中,只有l(wèi)eader給follower發(fā)心跳信息(心跳是沒有l(wèi)og-entry的Append Entries rpc),然后follower回復(fù)心跳消息。
Followers are passive: they issue no requests on their own but simply respond to requests from leaders and candidates.
在mongodb中,節(jié)點兩兩之間有心跳
Replica set members send heartbeats (pings) to each other every two seconds. If a heartbeat does not return within 10 seconds, the other members mark the delinquent member as inaccessible.
primary handover
在raft中,只有當leader收到來自term更高的節(jié)點的消息時,才會切換到follower狀態(tài)。如果出現(xiàn)網(wǎng)絡(luò)分割(network partition),那么這個過期的leader還會一直認為自己是leader
If a candidate or leader discovers
that its term is out of date, it immediately reverts to follower
state.
在mongodb中,primary在election timeout時間還沒有收到來自majority 節(jié)點的消息時,會主動切換成secondary。這樣可以避免過期的Primary(stale primary)繼續(xù)對外提供服務(wù),尤其是MongoDB允許writeConcern:1.
選舉過程 - 預(yù)投票
raft中,在election timeout超時后,立即會發(fā)起選舉,執(zhí)行以下操作
- 增加節(jié)點本地的 current term ,切換到candidate狀態(tài)
- 投自己一票
- 并行給其他節(jié)點發(fā)送 RequestVote RPCs
- 等待其他節(jié)點的回復(fù)
- 如果得到majority投票,成為leader
mongodb增加了一個預(yù)投票的過程(dry-run),即在不增加新的term的情況下先問問其他節(jié)點,是否可能給自己投票,得到大多數(shù)節(jié)點的肯定回復(fù)之后才會發(fā)起真正的選舉過程。其作用在于盡可能減少不必要的主從切換,這部分后面還會提到。
log replication
復(fù)制集中,各個節(jié)點數(shù)據(jù)的一致性是必須要解決的問題。而對于客戶端(應(yīng)用)而言,復(fù)制集則需要承諾已提交的數(shù)據(jù)不能回滾。
同步or異步
在帶著問題學習分布式系統(tǒng)之中心化復(fù)制集一文中,介紹了復(fù)制集中數(shù)據(jù)的兩種復(fù)制方式,并分析了各自的優(yōu)缺點。簡而言之,同步方式可靠性更高,但可用性更差,網(wǎng)絡(luò)延時更大;異步模式則恰好相反。
raft協(xié)議則是這兩種方式的折中,當log復(fù)制到了大多數(shù)的節(jié)點就可以向客戶端返回了。大多數(shù)節(jié)點既保證了數(shù)據(jù)的可靠性:數(shù)據(jù)不會被回滾;又保證了有較高的可用性:只有有超過一半節(jié)點存活整個系統(tǒng)就能正常工作。
MongoDB通過Write Concern選項將選擇權(quán)交給了用戶,用戶可以根據(jù)實際情況來選擇將數(shù)據(jù)復(fù)制到了多少節(jié)點再向客戶端返回。writeconcern有三個參數(shù)
- w:寫到多少節(jié)點即可向客戶端返回
- 1,默認值,即寫primary即可返回,性能最高,延遲最低
- majority,同raft,寫到大多數(shù)節(jié)點才返回
- tag set,寫到指定的節(jié)點才返回,用于特殊場景
- j:是否寫到j(luò)ournal(保證持久化)
- wtimeout:多長時間如果沒有寫到w個節(jié)點就向客戶端返回錯誤
由于默認寫到primary即可向客戶端返回,那么不難想到,如果oplog尚未同步到secondary,primary掛掉,那么新選舉出來的Primary可能沒有最新的已經(jīng)向客戶端確認的數(shù)據(jù),導(dǎo)致數(shù)據(jù)的回滾,后面會提到mongodb通過catchup來盡量避免回滾。
data flow
在帶著問題學習分布式系統(tǒng)之中心化復(fù)制集中也給出了兩種數(shù)據(jù)從primary到secondary的方式:主從模式,鏈式模式。其中,主從模式是priamry推送給所有的secondary,顯然raft就是這種模式。
而在MongoDB中,可以通過參數(shù)settings.chainingAllowed控制使用主從模式,還是鏈式模式。默認值為True,即默認情況下,mongodb中secondary可以從其他secondary同步數(shù)據(jù),這樣secondary可以選擇一個離自己最近(心跳延時最小的)節(jié)點來復(fù)制oplog,在MongoDB中,稱oplog的同步源為SyncSource。
push or pull
raft中,leader并行將數(shù)據(jù)push到follower。而在MongoDB中,primary將數(shù)據(jù)寫到local.oplog.rs,secondary定期從其SyncSource(參考上一節(jié),不一定是從priamry拉數(shù)據(jù),也可能是從其他secondary)讀取oplog,并應(yīng)用到本地。
深入淺出MongoDB復(fù)制一文中給出了一個oplog拉取的流程
MongoDB選擇了pull的策略,顯然會加大在writeConcern: majority時的延遲,但對于默認的鏈式復(fù)制,pull是更合適的,因為secondary更清楚自己的SyncSource。
append vs apply
在諸多共識算法中,都是將command封裝到有序、持久化的log當中,raft和MongoD也是如此。
對于raft,leader先將log先append到本地的log entries,然后等到收到majority節(jié)點的回復(fù)后再apply log到狀態(tài)機,如下入所示:
但是在mongodb中,即使客戶端要求writeconcern:majority,primary也是先apply,將變更作用到狀態(tài)機,再寫oplog。之后,secondary再從其SyncSource的local.oplog.rs collection 拉取oplog,本地apply,然后寫oplog。
MongoDB先Apply再寫oplog,以及異步復(fù)制的機制,會導(dǎo)致即使數(shù)據(jù)無法寫到大多數(shù)節(jié)點(可能primary與其他節(jié)點間網(wǎng)絡(luò)故障),即使向客戶端返回寫入失敗,寫到primary的數(shù)據(jù)也不會回滾。
catchup
catchup既與write concern有關(guān),也跟leader election有關(guān)。
mongodb中,有這么一個參數(shù)settings.catchUpTimeoutMillis, 其作用是
Time limit in milliseconds for a newly elected primary to sync (catch up) with the other replica set members that may have more recent writes.
The newly elected primary ends the catchup period early once it is fully caught up with other members of the set. During the catchup period, the newly elected primary is unavailable for writes from clients.
也就是說,在primary選舉出來之后,會有一段時間,讓primary嘗試去其他節(jié)點讀取到更新的寫操作(more recent)。直到追加到最新的oplog,或者超時,primary才進入工作狀態(tài)(接收客戶端寫請求)
究其原因,MongoDB允許用戶自定義writeconcern,且默認只要求寫到primary。因此選舉的時候即使得到了大多數(shù)節(jié)點的投票,且primary的數(shù)據(jù)在這些大多數(shù)節(jié)點中是最新的,但原來的primary可能沒有參與投票,那么就可能導(dǎo)致數(shù)據(jù)的回滾。catchup能夠盡量避免回滾的出現(xiàn),如果無法在settings.catchUpTimeoutMillis時間內(nèi)完成catchup,也會將回滾的內(nèi)容寫入一個rollback文件。
差異的思考
MongoDB作為一個分布式數(shù)據(jù)庫系統(tǒng),既要支持OLTP,又要支持OLAP,既要滿足水平伸縮,又要保證高可用、高可靠,還要支持分布式事務(wù)(Mongodb 4.x)。因此為了盡量滿足不同場景下的業(yè)務(wù)需求,MongoDB提供了大量的選項,供用戶選擇,更加靈活。對于復(fù)制集這一塊而言,選項包括但不限于:
- WriteConcern
- ReadConcern
- ReadPreference
- settings.chainingAllowed
- settings.catchUpTimeoutMillis
所以,作為MongoDB的用戶,首先得清楚這些可選項的意義,然后根據(jù)自己的業(yè)務(wù)需求,合理配置。
從這些選項的默認值以及MongoDB的實現(xiàn),個人覺得,在CAP這個問題上,MongoDB應(yīng)該是更傾向于A(availability,可用性)的。
而諸如鏈式復(fù)制,Leader Priority這些特性,在分布式系統(tǒng)的部署層面來說都是很有用的,比如multi datacenter,很多分布式存儲系統(tǒng)也支持同樣的特性。Raft協(xié)議雖然說是為工業(yè)實現(xiàn)提供了很好的指導(dǎo),但到具體的應(yīng)用,還是得有諸多的調(diào)整和完善。
dry-run or pre-vote
MongoDB中的預(yù)投票是對raft協(xié)議很好的改進,之前,我在看Raft論文的時候也想到了一些corner case,在論文中并沒有很清楚的闡述,但預(yù)投票能很好解決這些問題。
事實上,MongoDB中的預(yù)投票(dry-run)并不是獨創(chuàng)的,在raft協(xié)議的超長版解釋Consensus: Bridging Theory and Practice中,raft協(xié)議的作者就建議實現(xiàn)pre-vote來增加系統(tǒng)的魯棒性。而在Four modifications for the Raft consensus algorithm(PS,該文的作者就是MongoDB的開發(fā)者)詳細闡述了Pre-vote的原因以及實現(xiàn)方法。Pre-vote是為了防止一個隔離的follower不斷發(fā)起選舉 導(dǎo)致term值的激增,以及不必要的主從切換。
如上圖所以,系統(tǒng)由s1 s2 s3三個節(jié)點組成,其中s1是leader,另外兩個節(jié)點是follower。
pre-vote考慮的是這樣一種情況,(s2)與(s1 s3)之間出現(xiàn)了網(wǎng)絡(luò)分割(network partition),那么按照raft算法,s2會不斷的嘗試發(fā)起選舉,意味著不斷的增加term。那么當網(wǎng)絡(luò)自愈之后,s2將消息發(fā)送到s1 s3. 按照raft論文figure2 Rules for servers
If RPC request or response contains term T > currentTerm: set currentTerm = T, convert to follower
因此s1 會切換到follower, s1 s3 term修改為57,但s2的log 大概率是過舊的(out of date),因此s2無法獲得選舉,s1 s3會在election timeout后發(fā)起選舉,其中一個成為term 58的leader。
pre-vote 避免了term inflation,但更重要的是,避免了一次沒有必要的重新選舉: s1一定會切換到follower,然后s1或者s3再次發(fā)起選舉,在這個過程中,由于沒有l(wèi)eader,整個系統(tǒng)其實是不可用的(至少不可寫)。
references
一文搞懂raft算法
replication
MongoDB and Raft
MongoDB復(fù)制集技術(shù)內(nèi)幕:工作原理及新版本改進方向
MongoDB 高可用復(fù)制集內(nèi)部機制:Raft 協(xié)議
Consensus: Bridging Theory and Practice
Four modifications for the Raft consensus algorithm
轉(zhuǎn)載于:https://www.cnblogs.com/xybaby/p/10165564.html
總結(jié)
以上是生活随笔為你收集整理的Raft与MongoDB复制集协议比较的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 做梦梦到吃桃子是什么意思周公解梦
- 下一篇: 晚上做梦梦到虫子是怎么回事