從MongoDBReplicaSetHA看分散式系統讀寫一致性問題

張包峰發表於2013-11-08
副本集基礎
Replica Set是mongodb提供的一個去中心化的備份模式(同時mongodb還提供了主從部署和分片模式),每個mongod都可以是master,且副本集內會自動選舉出一個primary,其他都暫時為seconary,primary掛掉後會自動選舉出新的primary。副本集內所有mongod儲存的都是資料全集,secondary節點會從primary同步資料操作以保證自己的資料up-to-date。副本集有自己的選舉機制,相對是一種比較簡單的選舉,根據心跳、權重等因素選取一個可用的節點來做primary。

副本集  

副本集裡只有一個primary可以寫,保持嚴格的一致性(primary是嚴格一致性,secondary是最終一致性)。primary把log寫在oplog裡,secondary的節點非同步拷貝primary的oplog(為了便利,副本集內各自的oplog都可以互相獲得和拷貝),將operation作用在自己的資料集上。oplog裡對於資料集的操作,都是冪等的。mongod啟動的時候會產生一個預設大小的oplog,想要改變oplog
size需要在啟動前設定,啟動後再設定需要走另一個流程,比較麻煩些。
如果副本集是偶數的話,可以加一個arbiter,他不維護資料,只參加投票。member還有別的狀態,如delayed
member
hidden member
clients預設讀的是primary的資料集,讀secondary的資料集可以另外指定。既然secondary是根據oplog對自己的資料集非同步實施operation的,就一定存在延遲。
副本集資料同步存在兩種方式:
1. 初始化同步:對於新的沒有資料的member或者丟失了一部分歷史記錄的缺失資料集,拷貝現有副本集內某個member的整份資料。整個過程流程為先clone資料,然後apply all changes,最後建索引。
2. 不間斷同步:一般secondary都是從primay同步(定時ping primary檢視state)。
為了保證續航性(Durability),在單執行緒上提供了journaling,每次寫需要先寫進journal,然後再到資料集(預設需要64bit的mongodb2.0以上版本才有journal)。
對於多執行緒的Replication而言,mongodb用多執行緒批量寫來保證併發,批處理按namespace分組,每個namespace裡的併發寫是有序的。當使用批量寫的時候,mongodb不允許read。

使用journaling的時候,mongodb先把寫操作記錄存在記憶體裡,實施在journal裡。預設journal路徑為/data/db,啟動mongod的時候會預分配一定大小,裡面的journal files是append-only的。jfile的最大容量可以設定,當jfile大於1G時候,會建立新jfile。jfile的路徑也可以另外指定,指定到別的檔案系統加快讀寫速度。一旦mongodb完成了所有寫操作,這些jfile就會被刪除。
簡單描述下從記憶體到jfile再到disk資料集的過程。Journalling提供三種檢視,shared views,private views(只讀)和?。他們許可權不同。mongodb用批處理的方式,先把記憶體裡的寫操作以private view的方式刷到jfile裡,然後再把jfile裡的內容以share view的方式flush到磁碟上,此時資料集得以更新到最新。

總結
Mongodb通過副本集的方式提高可用性,一方面副本集是去中心化的模式,能夠自動檢測選舉新的primary,同步節點之間的資料,通過mongos路由路口,保證對client端透明;另一方面副本集儲存資料全集,只要你能容忍他的最終一致性,secondary節點一樣可以提供read。在Mongodb2.0之後的版本加入了journaling這個東西,更加增強了可靠性,保證當你的primary掛掉之後,前面幾次修改操作仍然被記錄在journal中,可以被還原也可以被撤銷,避免資料不一致或弄髒的情況。journaling其實類似很多分散式系統中採用的做法,涉及到記憶體,涉及到先寫到journal中,寫成功後再執行到真的資料集中。
本文嘗試通過mongodb副本集的分析,對分散式系統讀寫一致性問題做簡單的理解,理解有誤之處還請多包涵。

(全文完)


相關文章