MongoDB公司原名10gen,創立于2007年,在2013年收到一筆2.31億美元的融資后,公司市值評估已增至10億美元級別,這個高度是知名開源公司Red Hat(創建于1993年)20年的奮斗成果。
高性能、易擴展一直是MongoDB的立足之本,同時規范的文檔和接口更讓其深受用戶喜愛,這一點從分析DB-Engines的得分結果不難看出――僅僅1年時間,MongoDB就完成了第7名到第五名的提升,得分就從124分上升至214分,上升值是第四名PotgreSQL的兩倍,同時當下與PostgreSQL的得分也只相差16分不到。
點擊查看大圖
MongoDB能以如此速度發展,很大程度上歸結于許多傳統關系數據庫已無法應對當下數據處理的擴展性需求,雖然它們久經考驗,并具備不錯的性能及穩定性。然而區別于以往的使用方法,許多NoSQL都有著自己的限制,從而也導致了入門難的問題。這里我們為大家分享 嚴瀾的博文――如何搭建高效的MongoDB集群。
此前,我們有分享過該系列博文的第一部分,這里我們將為大家分享第二個部分――深入副本集內部機制及分片。
以下為博文:
該系列文章的第一部分介紹了副本集的配置,這個部分將深入研究一下副本集的內部機制。還是帶著副本集的問題來看吧!
Bully算法
MongDB副本集故障轉移功能得益于它的選舉機制。選舉機制采用了Bully算法,可以很方便從分布式節點中選出主節點。一個分布式集群架構中一般都有一個所謂的主節點,可以有很多用途,比如緩存機器節點元數據,作為集群的訪問入口等等。主節點有就有吧,我們干嘛要什么Bully算法?要明白這個我們先看看這兩種架構:
指定主節點的架構,這種架構一般都會申明一個節點為主節點,其他節點都是從節點,如我們常用的MySQL就是這樣。但是這樣架構我們在第一節說了整個集群如果主節點掛掉了就得手工操作,上架一個新的主節點或者從從節點恢復數據,不太靈活。
不指定主節點,集群中的任意節點都可以成為主節點。MongoDB也就是采用這種架構,一但主節點掛了其他從節點自動接替變成主節點。如下圖:
好了,問題就在這個地方,既然所有節點都是一樣,一但主節點掛了,怎么確定下一個主節點?這就是Bully算法解決的問題。
那什么是Bully算法,Bully算法是一種協調者(主節點)競選算法,主要思想是集群的每個成員都可以聲明它是主節點并通知其他節點。別的節點可以選擇接受這個聲稱或是拒絕并進入主節點競爭。被其他所有節點接受的節點才能成為主節點。節點按照一些屬性來判斷誰應該勝出。這個屬性可以是一個靜態ID,也可以是更新的度量像最近一次事務ID(最新的節點會勝出)。詳情請參考 NoSQL數據庫分布式算法的協調者競選還有 維基百科的解釋。
選舉
那么,MongDB是怎進行選舉的呢?官方這么描述:
We use a consensus protocol to pick a primary. Exact details will be spared here but that basic process is:
- get maxLocalOpOrdinal from each server.
- if a majority of servers are not up (from this server’s POV), remain in Secondary mode and stop.
- if the last op time seems very old, stop and await human intervention.
- else, using a consensus protocol, pick the server with the highest maxLocalOpOrdinal as the Primary.
大致翻譯過來為使用一致協議選擇主節點。基本步驟為:
這里提到了一個一致協議(其實就是bully算法),這個和數據庫的一致性協議還是有些區別,一致協議主要強調的是通過一些機制保證大家達成共識;而一致性協議強調的是操作的順序一致性,比如同時讀寫一個數據會不會出現臟數據。一致協議在分布式里有一個經典的算法叫“Paxos算法”,后續再介紹。
上面有個問題,就是所有從節點的最后操作時間都是一樣怎么辦?就是誰先成為主節點的時間最快就選誰。
選舉觸發條件
選舉不是什么時刻都會被觸發的,有以下情況可以觸發。
選舉還有個前提條件,參與選舉的節點數量必須大于副本集總節點數量的一半,如果已經小于一半了所有節點保持只讀狀態。日志將會出現:
can't see a majority of the set, relinquishing primary
1. 主節點掛掉能否人為干預?答案是肯定的。
可以通過replSetStepDown命令下架主節點。這個命令可以登錄主節點使用
db.adminCommand({replSetStepDown : 1})
如果殺不掉可以使用強制開關
db.adminCommand({replSetStepDown : 1, force : true})
或者使用 rs.stepDown(120)也可以達到同樣的效果,中間的數字指不能在停止服務這段時間成為主節點,單位為秒。
2. 設置一個從節點有比主節點有更高的優先級。
先查看當前集群中優先級,通過rs.conf()命令,默認優先級為1是不顯示的,這里標示出來
rs.conf();
{ "_id" : "rs0", "version" : 9, "members" : [ { "_id" : 0, "host" : "192.168.1.136:27017" }, { "_id" : 1, "host" : "192.168.1.137:27017" }, { "_id" : 2, "host" : "192.168.1.138:27017" } ] }
如果不想讓一個從節點成為主節點可以怎么操作?
當主節點不能和大部分從節點通訊。把主機節點網線拔掉,嘿嘿:)
優先級還可以這么用,如果我們不想設置什么hidden節點,就用secondary類型作為備份節點也不想讓他成為主節點怎么辦?看下圖,共三個節點分布在兩個數據中心,數據中心2的節點設置優先級為0不能成為主節點,但是可以參與選舉、數據復制。架構還是很靈活吧!
奇數
官方推薦副本集的成員數量為奇數,最多12個副本集節點,最多7個節點參與選舉。最多12個副本集節點是因為沒必要一份數據復制那么多份,備份太多反而增加了網絡負載和拖慢了集群性能;而最多7個節點參與選舉是因為內部選舉機制節點數量太多就會導致1分鐘內還選不出主節點,凡事只要適當就好。這個“12”、“7”數字還好,通過他們官方經過性能測試定義出來可以理解。具體還有哪些限制參考官方文檔 《 MongoDB Limits and Thresholds 》。 但是這里一直沒搞懂整個集群為什么要奇數,通過測試集群的數量為偶數也是可以運行的,參考這個文章http://www.itpub.net/thread-1740982-1-1.html。后來突然看了一篇 stackoverflow的文章終于頓悟了,mongodb本身設計的就是一個可以跨IDC的分布式數據庫,所以我們應該把它放到大的環境來看。
假設四個節點被分成兩個IDC,每個IDC各兩臺機器,如下圖。但這樣就出現了個問題,如果兩個IDC網絡斷掉,這在廣域網上很容易出現的問題,在上面選舉中提到只要主節點和集群中大部分節點斷開鏈接就會開始一輪新的選舉操作,不過MongoDB副本集兩邊都只有兩個節點,但是選舉要求參與的節點數量必須大于一半,這樣所有集群節點都沒辦法參與選舉,只會處于只讀狀態。但是如果是奇數節點就不會出現這個問題,假設3個節點,只要有2個節點活著就可以選舉,5個中的3個,7個中的4個……
心跳
綜上所述,整個集群需要保持一定的通信才能知道哪些節點活著哪些節點掛掉。MongoDB節點會向副本集中的其他節點每兩秒就會發送一次pings包,如果其他節點在10秒鐘之內沒有返回就標示為不能訪問。每個節點內部都會維護一個狀態映射表,表明當前每個節點是什么角色、日志時間戳等關鍵信息。如果是主節點,除了維護映射表外還需要檢查自己能否和集群中內大部分節點通訊,如果不能則把自己降級為secondary只讀節點。
同步
副本集同步分為初始化同步和keep復制。初始化同步指全量從主節點同步數據,如果主節點數據量比較大同步時間會比較長。而keep復制指初始化同步過后,節點之間的實時同步一般是增量同步。初始化同步不只是在第一次才會被處罰,有以下兩種情況會觸發:
那什么是oplog的大小?前面說過oplog保存了數據的操作記錄,secondary復制oplog并把里面的操作在secondary執行一遍。但是oplog也是mongodb的一個集合,保存在local.oplog.rs里;然而這個oplog是一個capped collection,也就是固定大小的集合,新數據加入超過集合的大小會覆蓋,所以這里需要注意,跨IDC的復制要設置合適的oplogSize,避免在生產環境經常產生全量復制。oplogSize 可以通過