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