1、MongoDB復制集(副本集)簡介
復制集模式(replicaSet mode),也叫副本集模式佳晶,指兩個及以上的mongo實例組合在一起,其中一個主節(jié)點可以讀寫讼载,剩下的節(jié)點可能是備節(jié)點轿秧、仲裁節(jié)點中跌、隱藏節(jié)點等等。一般生產(chǎn)環(huán)境部署為3個實例組成一個復制集淤刃,常見的有PSS(主備備)模式和PSA(主備仲裁)模式晒他。備節(jié)點不承擔寫任務,可以承擔讀任務逸贾。當主節(jié)點掛掉時陨仅,mongo的選舉機制會在該復制集中的其他備節(jié)點中選出主節(jié)點。
2铝侵、復制集部署
部署復制集的方法很簡單灼伤,可以采用mlaunch工具自動安裝MongoDB復制集,也可以根據(jù)實際需求拉起mongo進程咪鲜,然后配置復制集狐赡。在生產(chǎn)環(huán)境中一般會采用后者。下面我們就一起動手操作下疟丙,大致思路為:首先啟動所有成員的mongo的進程颖侄,然后使用rs.initiate命令將這些進程根據(jù)配置組成一個復制集。
(1)啟動所有的成員服務器享郊,需加上-replSet參數(shù)指定復制集名稱
nohup ./mongod --port 30011 --dbpath /data/db/mongodb/shard1/data1 --bind_ip 127.0.0.1,172.16.0.2 -replSet shard1 &
nohup ./mongod --port 30012 --dbpath /data/db/mongodb/shard1/data2 --bind_ip 127.0.0.1,172.16.0.2 -replSet shard1 &
nohup ./mongod --port 30013 --dbpath /data/db/mongodb/shard1/data3 --bind_ip 127.0.0.1,172.16.0.2 -replSet shard1 &
注:nohup和&的作用是拉起mongo進程后不退出览祖。由于筆者只有一臺虛擬機,所以只能通過不同的端口和目錄來啟動mongo進程作為演示炊琉。在真實生產(chǎn)環(huán)境中展蒂,一個mongod數(shù)據(jù)進程應該獨占一臺虛擬機。
執(zhí)行上述命令后苔咪,我們得到三個mongo進程锰悼,指定的復制集名字叫“shard1”:
(2)配置復制集
①進入上述任意mongo實例
./mongo --port 30011
②定義復制集配置
var config={_id:"shard1", members:[{_id:1, host:"172.16.0.2:30011"}, {_id:2, host:"172.16.0.2:30012"}, {_id:3, host:"172.16.0.2:30013"}]}
注:配置中的_id字段就是啟動mongo進程時的replSet指定的復制集名稱。
③復制集初始化
rs.initiate(config)
注:只需對復制集內(nèi)一個成員調(diào)用rs.initiate就可以了团赏,該命令會自動將配置同步到其他members箕般。復制集初始化以后,這些成員會根據(jù)配置自動選出一個主節(jié)點舔清,然后就可以正常處理業(yè)務了隘世。如下圖所示,復制集初始化完成后,當前實例由單節(jié)點變?yōu)榱薙ECONDARY:
(3)修改復制集配置的方法
如果要修改復制集的配置,可以使用rs.reconfig()來重新加載配置(覆蓋原來的配置)扎唾,原理和rs.initiate()類似澄步。
如果是添加成員和刪除成員,可以使用rs.add()和rs.remove()目锭,當然也可以使用rs.reconfig()來實現(xiàn)评汰。
(4)查看復制集信息
通過rs.status()可以查看當前復制集所有成員信息以及狀態(tài)纷捞。
3、復制集選舉機制和成員角色
3.1 選舉機制
復制集中很重要的一個概念是“大多數(shù)(majority)原則”:選擇主節(jié)點時需要由大多數(shù)節(jié)點同意被去;寫操作備復制到大多數(shù)成員時這個寫操作才是安全的主儡。大多數(shù)被定義為“復制集中一半以上的成員”,如果復制集中有些成員掛了或者是不可用惨缆,并不會影響到“大多數(shù)”糜值,因為大多數(shù)是基于復制集的配置來計算的。例如三個節(jié)點組成的復制集中坯墨,如果兩個節(jié)點同時掛掉了寂汇,剩余的一個節(jié)點無法成為主,只能是備節(jié)點捣染,無法承接寫操作的業(yè)務骄瓣。
MongoDB復制集采用大多數(shù)原則是為了防止“腦裂”,即防止少數(shù)的節(jié)點由于網(wǎng)絡原因與大多數(shù)節(jié)點失聯(lián)耍攘,然后自己選出了一個主榕栏,這樣復制集就出現(xiàn)了雙主情況,都可以寫入數(shù)據(jù)蕾各,復制集的數(shù)據(jù)就會發(fā)生混亂扒磁。
每個成員在復制集中都有自己的選舉權(quán)重,權(quán)重越高越容易被選為主節(jié)點示损。成員的權(quán)重在加入復制集時由priority參數(shù)設置渗磅。
通過思考復制集的選舉規(guī)則,我們可以推理出一下兩個部署MongoDB復制集時的兩個基本策略:
(1)一個復制集的成員個數(shù)最好是奇數(shù)检访,防止兩個成員拿到相同的票數(shù)而遲遲選不出主始鱼;
(2)一個復制集的最高選舉權(quán)重成員最好有多個,且權(quán)重相等脆贵,防止故障恢復后又發(fā)生主備切換医清。比如說某權(quán)重最大的節(jié)點故障修復后重新加入到復制集,它發(fā)起了選舉請求卖氨,由于它的選舉權(quán)重最大会烙,就變成了主節(jié)點,這樣就多了一次主備切換筒捺,對業(yè)務會有些影響柏腻。
3.2 主節(jié)點降備
有時候為了維護,需要將主節(jié)點降備系吭。有多種方式可以將主降為備五嫂。注意,我們無法強制將某個成員變?yōu)橹鞴?jié)點,除非對復制集做適當?shù)呐渲谩?/p>
主節(jié)點降級為備節(jié)點的命令為:rs.stepDown()沃缘,不指定時間即默認為60s躯枢,在60s內(nèi)該節(jié)點不能再次選為主節(jié)點。
根據(jù)之前的描述來看槐臀,可以總結(jié)出復制集發(fā)生主備切換的觸發(fā)條件有:
(1)主節(jié)點上執(zhí)行了rs.stepDonw()降備锄蹂;
(2)主節(jié)點掛掉或失聯(lián),剩余大多數(shù)節(jié)點重新選出新主水慨;
(3)優(yōu)先級更高的節(jié)點加入了復制集中得糜。
3.3 復制集其他成員角色
MongoDB復制集除了主節(jié)點和備節(jié)點外,還可以存在其他角色讥巡。
(1)仲裁者
仲裁者的作用就是參與選舉掀亩,并不保存數(shù)據(jù),也會成為主欢顷,也不會為客戶端提供服務槽棍,它一般只是為了湊夠奇數(shù)個節(jié)點,可以部署在配置較差的服務器上抬驴。如果可能炼七,應該將仲裁節(jié)點部署在單獨的故障域中,這樣它就可以以“外部視角”來看待復制集中的成員了布持。
啟動仲裁者與啟動普通mongod進程的方式相同豌拙,然后使用rs.addArb()將仲裁者添加到復制集中:
rs.addArb("server-4")
也可以在成員配置中指定arbiterOnly選項,例如:
rs.add({_id:3, host:"server-4", arbiterOnly:true})
成員一旦以仲裁者身份加入到復制集中题暖,它就永遠只能是仲裁者按傅。一個復制集內(nèi)最多只能有一個仲裁者。如果有條件胧卤,盡可能在復制集中使用奇數(shù)個數(shù)據(jù)不成員唯绍,而不要使用仲裁者。例如用PSS部署模式代替PSA模式枝誊。這對于生產(chǎn)環(huán)境的可靠性和運維有著很重要的作用况芒。
(2)隱藏者
為了隱藏某個服務器,可以在配置中指定hidden:true叶撒,只有選舉權(quán)重為0的實例才能被掩藏绝骚。
客戶端不會向隱藏成員請求數(shù)據(jù),隱藏成員也不會作為復制源祠够。因此压汪,可以將不夠強大的服務器隱藏起來。隱藏成員有著該復制集的全量數(shù)據(jù)古瓤,當復制集內(nèi)其他成員的數(shù)據(jù)文件都被破壞了蛾魄,可以通過隱藏成員上保存的數(shù)據(jù)進行環(huán)境修復。
(3)延遲復制者
數(shù)據(jù)可能會因為操作錯誤而遭受毀滅性破壞,可能有人在主節(jié)點上執(zhí)行了誤刪除命令滴须,或者剛上線的應用代碼包含了致命bug,污染了所有mongo數(shù)據(jù)叽奥。為了防止這類問題扔水,可以使用slaveDelay設置一個延遲復制者。延遲復制者的數(shù)據(jù)會比主節(jié)點延遲指定的時間朝氓,如果主節(jié)點的數(shù)據(jù)受到了破壞或者污染魔市,可以通過延遲復制者將數(shù)據(jù)恢復到指定時間之前。
同樣的赵哲,延遲復制者的選舉權(quán)重必須設置為0待德。
(4)不創(chuàng)建索引者
有時,備份節(jié)點并不需要與主節(jié)點擁有相同的索引枫夺,甚至可以沒有索引将宪,可以在成員配置中指定buildIndexs:false。主要使用場景為只做數(shù)據(jù)備份或者是離線的批量任務橡庞。
同樣的较坛,不創(chuàng)建索引者的選舉權(quán)重必須設置為0。
4 復制集數(shù)據(jù)同步
4.1 oplog簡介
MongoDB復制集內(nèi)成員的數(shù)據(jù)同步復制是通過一個日志來存儲寫操作的扒最,這個日志就叫做oplog丑勤。oplog記錄了主節(jié)點上的每一次寫操作,它是一個固定集合(Capped Collection)吧趣,其他節(jié)點通過查詢該集合就可以同步數(shù)據(jù)法竞。每個數(shù)據(jù)成員維護著自己的oplog,也可以作為同步源給其他成員使用强挫。
下面是查看oplog的例子岔霸,其中op:"i"表示這是一個插入操作,是向test.foo集合中插入了一條數(shù)據(jù)纠拔。
oplog重要字段說明如下表所示:
字段 | 含義 |
---|---|
ts | 8字節(jié)的時間戳秉剑,由4字節(jié)unix timestamp + 4字節(jié)自增計數(shù)表示。這個值很重要稠诲,在選舉新primary時侦鹏,會選擇ts最大的那個secondary作為新的primary。 |
op | 1字節(jié)的操作類型臀叙,可以是如下幾種情形之一: i略水,表示insert; u劝萤,表示update渊涝; d,表示delete; c跨释,表示command胸私; n,表示no op鳖谈,即空操作岁疼,其會定期執(zhí)行以確保時效性。 |
ns | 操作所在namespace缆娃,由數(shù)據(jù)庫名+集合名構(gòu)成 |
o | 操作所對應的document捷绒,即當前操作的內(nèi)容,比如說更新操作時要更新的字段和值贯要。 |
o2 | 僅限于update操作時暖侨,表示更新條件。 |
4.2 oplog的增長速度
oplog是固定大小的崇渗,它只能保存特定數(shù)據(jù)的操作日志字逗,oplog使用空間的增長速度跟系統(tǒng)處理寫請求的速度相當。如果單次操作影響了多個文檔(比如說刪除了多個文檔或者更新了多個文檔)显押,則oplog就會產(chǎn)生多條操作日志扳肛。如果存在大批量的操作,oplog有可能很快會被寫滿乘碑。
在生產(chǎn)環(huán)境中挖息,需要根據(jù)實際業(yè)務需求設置oplog的大小。若一個復制集上的業(yè)務量預估會很大兽肤,則需要在第一次部署生產(chǎn)環(huán)境的時候就將oplog設置大一些套腹。因為后期再調(diào)整其大小會比較麻煩。一般將oplog大小設置成能夠存儲一天的寫操作較為合理资铡,若條件有限电禀,至少應該保證oplog大小能夠保存10小時以上的寫操作。當出現(xiàn)數(shù)據(jù)丟失問題時笤休,可以通過分析oplog找回數(shù)據(jù)尖飞。或者需要對復制集內(nèi)某數(shù)據(jù)成員進行運維時店雅,可以將備節(jié)點以單節(jié)點方式啟動單獨運維政基,再次加入到復制集中可以繼續(xù)增量同步,不會因為主節(jié)點的oplog的時間窗口過段而被老化闹啦,防止走初始化同步消耗大量時間沮明。
4.3 復制集數(shù)據(jù)同步過程
primary節(jié)點寫入數(shù)據(jù)產(chǎn)生oplog,secondary通過讀取primary的oplog得到復制信息窍奋,開始復制數(shù)據(jù)并且將數(shù)據(jù)寫入到自己的oplog中荐健。如果某個操作失斀闯(只有當同步源的數(shù)據(jù)損壞或者數(shù)據(jù)與主節(jié)點不一致時才可能發(fā)生),則備節(jié)點停止從當前數(shù)據(jù)源復制數(shù)據(jù)江场。如果某個備節(jié)點重啟了纺酸,會從上次自己最后一個oplog去主找同步點。
oplog很重要的一個特性是冪等性扛稽,即一條oplog執(zhí)行多次與執(zhí)行一次的效果是相等的吁峻。
4.4 復制集初始化同步
限于篇幅,此處不再贅述在张,大家可參考阿里專家張友東的博客:MongoDB同步原理解析。
初始化同步的注意事項有:
(1)如果要跟蹤初始化同步過程矮慕,最好的方法是查詢服務器日志帮匾。
(2)初始化同步操作簡單,但是速度太慢痴鳄,遠不如從備份中恢復瘟斜。
(3)克隆可能損壞同步源的工作集。實際部署后會有一個頻繁使用的數(shù)據(jù)子集在內(nèi)存中痪寻,執(zhí)行初始化同步會強制將當前成員的所用數(shù)據(jù)分頁加載到內(nèi)存中螺句,者會導致熱點數(shù)據(jù)不能常駐內(nèi)存,進而導致數(shù)據(jù)庫請求變慢橡类。不過對于較小的數(shù)據(jù)集合性能比較好的服務器蛇尚,初始化同步是個簡單易用的選項。
(4)初始化同步如果耗時太長顾画,新成員就會與同步源脫節(jié)取劫,導致新成員的數(shù)據(jù)同步速度趕不上同步源的變化速度。這個問題沒有好的解決辦法研侣,只能在不忙的時候執(zhí)行初始化同步谱邪。這是就可以看到讓主節(jié)點使用足夠大的oplog保持足夠多的操作是很有必要的!
4.5 回滾
如果主節(jié)點執(zhí)行了一次寫請求后掛了庶诡,但是備節(jié)點還沒有來得及復制這次操作惦银,那么新選舉出來的主節(jié)點就會漏掉這次寫操作,這時就會執(zhí)行回滾過程末誓。
如果回滾的數(shù)據(jù)大于300MB扯俱,或者要回滾30分鐘以上的操作,回滾就是失敗基显。對于回滾失敗的節(jié)點蘸吓,必須要重新同步。這種情況最常見的原因是備節(jié)點遠遠落后于主節(jié)點撩幽,而這時主節(jié)點卻掛了库继,落后很久的備節(jié)點升為了主箩艺。為了防止這種情況,要保證復制集內(nèi)各個數(shù)據(jù)節(jié)點之間的網(wǎng)絡要好宪萄,并且磁盤要足夠好艺谆。
5 復制集的好處
(1)可靠性
引入復制集最開始的初衷是通過數(shù)據(jù)冗余來解決故障中斷,提供不間斷的數(shù)據(jù)庫服務拜英,提高產(chǎn)品可靠性静汤。當主節(jié)點掛掉時,復制集剩余成員可以很快地重新選出主節(jié)點居凶,繼續(xù)提供服務虫给。所以復制集有著與生俱來的可靠性特點。
(2)讀寫分離
為了防止復制集內(nèi)主節(jié)點的壓力過大侠碧,可以將業(yè)務中對時效性要求不高的查詢業(yè)務放在備節(jié)點上執(zhí)行抹估。特別是比較復雜的聚合操作和報表統(tǒng)計操作,這些操作往往很耗時弄兜,放在備節(jié)點操作是個很好的選擇药蜻。
(3)功能隔離
MongoDB的備份(不管采用dump方式還是拷貝原始數(shù)據(jù)文件的方式),我們可以在備節(jié)點上執(zhí)行替饿,減少主節(jié)點的壓力(cpu语泽、內(nèi)存、磁盤IO等)视卢;
復制集可以讓我們很方便的對生產(chǎn)環(huán)境進行運維踱卵。比如說將對一個非常大的表建立索引,若以阻塞方式建立索引腾夯,那勢必會阻塞正常業(yè)務颊埃;若以非阻塞方式建立索引,那建立完索引可能需要持續(xù)很長的時間蝶俱。有了復制集我們就可以這樣做:①將一個備節(jié)點從復制集中移除班利;②以單節(jié)點模式啟動該節(jié)點,然后以阻塞方式快速建立完索引榨呆;③將該節(jié)點再以復制集方式啟動罗标,加入到原復制集中;④依次對所有備節(jié)點都執(zhí)行這樣的操作积蜻;⑤對主節(jié)點進行降備闯割,然后再執(zhí)行同樣的操作。
(4)跨地區(qū)分發(fā)
復制集可以將數(shù)據(jù)分布在不同的地區(qū)竿拆,這為異地容災提供了很便捷的方法宙拉。比如說兩地三中心容災架構(gòu),我們就可以將復制集內(nèi)兩個節(jié)點放在一個地區(qū)丙笋,將另一個節(jié)點放到另一個地區(qū)中谢澈。
這樣部署方式要注意網(wǎng)絡帶寬煌贴,并且需考慮兩地實際距離。因為一次請求往返一次锥忿,那么每增加150公里就會多1毫秒的延遲牛郑。
(5)oplog便于運維
復制集內(nèi)的oplog存儲了數(shù)據(jù)庫最近時間段內(nèi)的所有寫操作,當業(yè)務出現(xiàn)數(shù)據(jù)異常時敬鬓,通過分析oplog很可能就得到了答案淹朋,并且根據(jù)oplog也可以修復一定的數(shù)據(jù)。
6 尾聲
本文給大家介紹了MongoDB復制集的安裝部署钉答、成員角色础芍、選舉機制、同步原理数尿、特點優(yōu)勢等者甲,相信大家有了一個基本的了解。在下一章我們一起學習MongoDB的分片(sharding)功能砌创,然后搭建一個分片集群,大家可以領略到MongoDB極強的水平擴展能力鲫懒。