在一文搞懂raft算法一文中,從raft論文出發,詳細介紹了raft的工做流程以及對特殊狀況的處理。但算法、協議這種偏抽象的東西,僅僅看論文仍是比較難以掌握的,須要看看在工業界的具體實現。本文關注MongoDB是如何在複製集中使用raft協議的,對raft協議作了哪些擴展。html
閱讀本文,須要對MongoDB複製集replication有必定認識,特別是replicat set protocol version。git
在帶着問題學習分佈式系統之中心化複製集一文中,介紹了中心化副本控制協議。在raft(mongodb pv1)中,也是經過先選舉出leader(primary),而後經過leader(primary)管理整個複製集。github
在3.2以及以後的版本中,mongodb默認使用protocol version 1。從官方的一些資料、視頻能夠看到,這個是一個raft-like的協議。本文主要從leader-election和log replication這兩個角度來對比mongodb rs pv1與raft,並試圖分析差別的緣由。web
須要注意的是,本文全部對MongoDB複製集的分析都是基於MongoDb3.4算法
本文地址:http://www.javashuo.com/article/p-xmvvskpj-bb.htmlmongodb
首先對raft協議中leader election作幾點總結:數據庫
爲了簡化協議,使得raft更容易理解,raft中全部節點都能發起選舉、參與投票。但在MongoDB中,有更爲豐富的選舉控制策略,咱們從Replica Set Configuration就能看出來,replica set中的節點能夠配置如下屬性網絡
members: [ { _id: <int>, host: <string>, arbiterOnly: <boolean>, buildIndexes: <boolean>, hidden: <boolean>, priority: <number>, tags: <document>, slaveDelay: <int>, votes: <number> }, ... ],
A number that indicates the relative eligibility of a member to become a primary.
priority爲0時是不能發起選舉的。這裏再單獨強調一下priority,mongodb中app
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()修改節點的優先級的時候,會觸發從新選舉。整個複製集會不斷髮起選舉,直到最高優先級的節點成爲primary。固然,在選舉-投票的過程當中,仍是必須知足候選者數據足夠新的約束。
priority頗有用,好比在multi datacenter deploy的狀況下,咱們可能根據用戶的分佈狀況來肯定primary在哪一個datacenter。
raft中,只有leader給follower發心跳信息(心跳是沒有log-entry的Append Entries rpc),而後follower回覆心跳消息。
Followers are passive: they issue no requests on their own but simply respond to requests from leaders and candidates.
在mongodb中,節點兩兩之間有心跳
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.
在raft中,只有當leader收到來自term更高的節點的消息時,纔會切換到follower狀態。若是出現網絡分割(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 節點的消息時,會主動切換成secondary。這樣能夠避免過時的Primary(stale primary)繼續對外提供服務,尤爲是MongoDB容許writeConcern:1.
raft中,在election timeout超時後,當即會發起選舉,執行如下操做
mongodb增長了一個預投票的過程(dry-run),即在不增長新的term的狀況下先問問其餘節點,是否可能給本身投票,獲得大多數節點的確定回覆以後纔會發起真正的選舉過程。其做用在於儘量減小沒必要要的主從切換,這部分後面還會提到。
複製集中,各個節點數據的一致性是必需要解決的問題。而對於客戶端(應用)而言,複製集則須要承諾已提交的數據不能回滾。
在帶着問題學習分佈式系統之中心化複製集一文中,介紹了複製集中數據的兩種複製方式,並分析了各自的優缺點。簡而言之,同步方式可靠性更高,但可用性更差,網絡延時更大;異步模式則剛好相反。
raft協議則是這兩種方式的折中,當log複製到了大多數的節點就能夠向客戶端返回了。大多數節點既保證了數據的可靠性:數據不會被回滾;又保證了有較高的可用性:只有有超過一半節點存活整個系統就能正常工做。
MongoDB經過Write Concern選項將選擇權交給了用戶,用戶能夠根據實際狀況來選擇將數據複製到了多少節點再向客戶端返回。writeconcern有三個參數
w
個節點就向客戶端返回錯誤因爲默認寫到primary便可向客戶端返回,那麼不難想到,若是oplog還沒有同步到secondary,primary掛掉,那麼新選舉出來的Primary可能沒有最新的已經向客戶端確認的數據,致使數據的回滾,後面會提到mongodb經過catchup
來儘可能避免回滾。
在帶着問題學習分佈式系統之中心化複製集中也給出了兩種數據從primary到secondary的方式:主從模式,鏈式模式。其中,主從模式是priamry推送給全部的secondary,顯然raft就是這種模式。
而在MongoDB中,能夠經過參數settings.chainingAllowed控制使用主從模式,仍是鏈式模式。默認值爲True,即默認狀況下,mongodb中secondary能夠從其餘secondary同步數據,這樣secondary能夠選擇一個離本身最近(心跳延時最小的)節點來複制oplog,在MongoDB中,稱oplog的同步源爲SyncSource。
raft中,leader並行將數據push到follower。而在MongoDB中,primary將數據寫到local.oplog.rs,secondary按期從其SyncSource(參考上一節,不必定是從priamry拉數據,也多是從其餘secondary)讀取oplog,並應用到本地。
深刻淺出MongoDB複製一文中給出了一個oplog拉取的流程
MongoDB選擇了pull的策略,顯然會加大在writeConcern: majority時的延遲,但對於默認的鏈式複製,pull是更合適的,由於secondary更清楚本身的SyncSource。
在諸多共識算法中,都是將command封裝到有序、持久化的log當中,raft和MongoD也是如此。
對於raft,leader先將log先append到本地的log entries,而後等到收到majority節點的回覆後再apply log到狀態機,以下入所示:
可是在mongodb中,即便客戶端要求writeconcern:majority,primary也是先apply,將變動做用到狀態機,再寫oplog。以後,secondary再從其SyncSource的local.oplog.rs collection 拉取oplog,本地apply,而後寫oplog。
MongoDB先Apply再寫oplog,以及異步複製的機制,會致使即便數據沒法寫到大多數節點(可能primary與其餘節點間網絡故障),即便向客戶端返回寫入失敗,寫到primary的數據也不會回滾。
catchup既與write concern有關,也跟leader election有關。
mongodb中,有這麼一個參數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嘗試去其餘節點讀取到更新的寫操做(more recent)。直到追加到最新的oplog,或者超時,primary才進入工做狀態(接收客戶端寫請求)
究其緣由,MongoDB容許用戶自定義writeconcern,且默認只要求寫到primary。所以選舉的時候即便獲得了大多數節點的投票,且primary的數據在這些大多數節點中是最新的,但原來的primary可能沒有參與投票,那麼就可能致使數據的回滾。catchup可以儘可能避免回滾的出現,若是沒法在settings.catchUpTimeoutMillis
時間內完成catchup,也會將回滾的內容寫入一個rollback文件。
MongoDB做爲一個分佈式數據庫系統,既要支持OLTP,又要支持OLAP,既要知足水平伸縮,又要保證高可用、高可靠,還要支持分佈式事務(Mongodb 4.x)。所以爲了儘可能知足不一樣場景下的業務需求,MongoDB提供了大量的選項,供用戶選擇,更加靈活。對於複製集這一塊而言,選項包括但不限於:
因此,做爲MongoDB的用戶,首先得清楚這些可選項的意義,而後根據本身的業務需求,合理配置。
從這些選項的默認值以及MongoDB的實現,我的以爲,在CAP這個問題上,MongoDB應該是更傾向於A(availability,可用性)的。
而諸如鏈式複製,Leader Priority這些特性,在分佈式系統的部署層面來講都是頗有用的,好比multi datacenter,不少分佈式存儲系統也支持一樣的特性。Raft協議雖說是爲工業實現提供了很好的指導,但到具體的應用,仍是得有諸多的調整和完善。
MongoDB中的預投票是對raft協議很好的改進,以前,我在看Raft論文的時候也想到了一些corner case,在論文中並無很清楚的闡述,但預投票能很好解決這些問題。
事實上,MongoDB中的預投票(dry-run)並非首創的,在raft協議的超長版解釋Consensus: Bridging Theory and Practice中,raft協議的做者就建議實現pre-vote來增長系統的魯棒性。而在Four modifications for the Raft consensus algorithm(PS,該文的做者就是MongoDB的開發者)詳細闡述了Pre-vote的緣由以及實現方法。Pre-vote是爲了防止一個隔離的follower不斷髮起選舉 致使term值的激增,以及沒必要要的主從切換。
如上圖因此,系統由s1 s2 s3三個節點組成,其中s1是leader,另外兩個節點是follower。
pre-vote考慮的是這樣一種狀況,(s2)與(s1 s3)之間出現了網絡分割(network partition),那麼按照raft算法,s2會不斷的嘗試發起選舉,意味着不斷的增長term。那麼當網絡自愈以後,s2將消息發送到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後發起選舉,其中一個成爲term 58的leader。
pre-vote 避免了term inflation,但更重要的是,避免了一次沒有必要的從新選舉: s1必定會切換到follower,而後s1或者s3再次發起選舉,在這個過程當中,因爲沒有leader,整個系統實際上是不可用的(至少不可寫)。
一文搞懂raft算法
replication
MongoDB and Raft
MongoDB複製集技術內幕:工做原理及新版本改進方向
MongoDB 高可用複製集內部機制:Raft 協議
Consensus: Bridging Theory and Practice
Four modifications for the Raft consensus algorithm