1、 複本集的結構及原理
複本集包括三種節點:主節點、從節點、仲裁節點。
主節點負責處理用戶端請求,讀、寫資料, 記錄在其上所有操作的oplog;
從節點定期輪詢主節點擷取這些操作,然後對自己的資料副本執行這些操作,從而保證從節點的資料與主節點一致。預設情況下,從節點不支援外部讀取,但可以設定;
複本集的機制在於主節點出現故障的時候,餘下的節點會選舉出一個新的主節點,從而保證系統可以正常運行。
仲裁節點不複製資料,僅參與投票。由於它沒有訪問的壓力,比較空閑,因此不容易出故障。由於複本集出現故障的時候,存活的節點必須大於複本集節點總數的一半,否則無法選舉主節點,或者主節點會自動降級為從節點,整個複本集變為唯讀。因此,增加一個不容易出故障的仲裁節點,可以增加有效選票,降低整個複本集停用風險。仲裁節點可多於一個。
2、 搭建複本集
1) 使用同一複本集名稱啟動所有節點執行個體
樣本:
mongod --port 40000 --dbpath rs0\data\ --logpath rs0\log\rs0.log --replSet myset
mongod --port 40001 --dbpath rs1\data\ --logpath rs1\log\rs1.log --replSet myset
……
2) 在任意節點進行複本集初始化
樣本:
mongo jsj-1306-201:40000
>use admin
> config={_id:"myset",members:[
{_id:0,host:"jsj-1306-201:40000","priority":2},
{_id:1,host:"jsj-1306-201:40001"},
{_id:2,host:"jsj-1306-201:40002",arbiterOnly:true}
]}
>rs.initiate(config);
3) 查閱複本集
>rs.conf()
或者
>rs.status()
4) 增添節點
添加節點,需要在主節點進行
PRIMARY>>rs.add(hostname:port)
5) 刪減節點
刪減節點,需要在主節點進行
PRIMARY>rs.remove(hostname:port)
3、 測試複本集
1) 測試資料複製
首先在主節點插入記錄
myset:PRIMARY> use test
switched to db test
myset:PRIMARY> for(var i=1;i<=1000;i++) db.table1.save({id:i,"test1":"testval1"}
);
然後串連某一從節點,讀取該節點上的資料:
myset:SECONDARY> use test
switched to db test
myset:SECONDARY> db.table1.find().count()
可知資料同步成功
2) 測試故障切換
在任意一節點,觀察複本集情況:
>rs.status()
然後關閉另一節點,再觀察
>rs.status()
只要餘下的節點超過總數的一半,那麼複本集可以自動進行故障切換:
如果關閉的是主節點,那麼複本集選出新的主節點;如果關閉的是從節點,那麼主節點仍然保持運行。
如果餘下節點小於或等於總節點數的一半,則不管關閉的是不是主節點,複本集中都不再存在主節點,原來的主節點會降級成為從節點。此時整個複本集呈現唯讀狀態。
4、 節點數量
複本集至少需要兩個節點,至多12個節點。其中一個是主節點,其餘的都是從節點和仲裁節點。具備投票權的節點至多為7個。從節點越多,複製的壓力就越大,備份太多反而增加了網路負載和拖慢了叢集效能。
MongoDB官方推薦節點數量為奇數。主要在於複本集常常為分布式,可能位於不同的IDC。如果為偶數,可能出現每個IDC的節點數一樣,從而如果網路故障,那麼每個IDC裡的節點都無法選出主節點,導致全部停用情況。比如,節點數為4,分處於2個IDC,現在IDC之間的網路出現故障,每個IDC裡的節點都沒有大於2,所以複本集沒有主節點,變成唯讀。
5、 從節點的類型
Secondary:標準從節點,可複製,可投票,可被選為主節點
Secondary-Only:不能成為主節點,只能作為從節點,防止一些效能不高的節點成為主節點。設定方法是將其priority = 0
Hidden:這類節點是不能夠被用戶端制定IP引用,也不能被設定為主節點,但是可以投票,一般用於備份資料。
Delayed:可以指定一個時間延遲從primary節點同步資料。主要用於備份資料,如果即時同步,誤刪除資料馬上同步到從節點,恢複又恢複不了。
樣本:
cfg= {
"_id" : <num>,
"host" : <hostname:port>,
"priority" : 0,
"slaveDelay" : <seconds>,
"hidden" : true
}
rs.initiate(cfg);
Non-Voting:沒有選舉權的secondary節點,純粹的備份資料節點。設定方法是將其votes = 0。之所以設定這種節點,是照顧複本集最多12個節點,但有投票權的節點最多7個節點的要求。
樣本:
PRIMARY>cfg = rs.conf()
PRIMARY>cfg.members[3].votes = 0
PRIMARY>cfg.members[4].votes = 0
PRIMARY>cfg.members[5].votes = 0
PRIMARY>rs.reconfig(cfg)
6、 讀寫分離
從節點預設情況下不能讀取,但可以設定此選項:
SECONDARY> db.getMongo().setSlaveOk();
這樣,讀取部分可以使用這些開啟了讀取選項的從節點,從而減輕主節點的負荷。
至於寫入,永遠只有主節點才可以進行。
7、 最終一致性
資料的寫入在主節點進行,將操作記錄進日誌oplog,從節點定期輪詢主節點,擷取oplog,然後對自己的資料副本執行這些操作,從而保證從節點的資料與主節點一致,因此有一定時間的滯後是必然的,MongoDB追求的是最終一致性。
既然“滯後”不可避免,需要做的就是儘可能減小這種滯後,主要涉及到以下幾點:
網路延遲:這是所有分布式系統都存在的問題。我們能做的就是儘可能減小複本集節點之間的網路延遲。
磁碟輸送量:secondary節點上資料刷入磁碟的速度比primary節點上慢的話會導致secondary節點很難跟上primary節點的節奏。
並發:並發大的情況下,primary節點上的一些耗時操作會阻塞secondary節點的複製操作,導致複製操作跟不上主節點的寫入負荷。解決方案是通過設定作業的write concern(參看這裡:http://docs.mongodb.org/manual/core/write-concern/#replica-set-write-concern)預設的複本集中寫入操作只關心primary節點,但是可以指定寫入操作同時傳播到其他secondary節點,代價就是嚴重影響叢集的並發性。
注意:而且這裡還存在一個問題如果,如果寫入操作關心的某個節點宕機了,那麼操作將會一直被阻塞直到節點恢複。
適當的write concern:我們為了提高叢集寫操作的輸送量經常會將writer concern設定為unacknowledged write concern,這導致primary節點的寫操作很快而secondary節點複製操作跟不上。解決方案和第三點是類似的就是在效能和一致性之間做權衡。
8、 複本集停用應對之道
當節點發生故障,或者因為網路原因失聯,造成餘下的節點小於等於複本集總節點數的一半,那麼整個複本集中都不再存在主節點,原來的主節點會降級成為從節點。此時整個複本集呈現唯讀狀態,也即不再可用。
當出現這種癱瘓情況,目前我還沒找到將某個從節點切換為主節點的方法,也許本來就不存在這種方法。因為這和MongoDB的Primary選舉策略有關:如果情況不是Secondary宕機,而是網路斷開,那麼可能出現位於不同IDC的節點各自選取自己為Primary。這樣在網路恢複後就需要處理複雜的一致性問題。而且斷開的時間越長,時間越複雜。所以MongoDB選擇的策略是如果叢集中存活節點數量不夠,則不選取Primary。
所以
1) 一方面要極力避免出現這種存活節點不夠半數的情況,在規劃複本集的時候就注意:
設定仲裁節點。
節點總數為奇數,且主節點所在的IDC,擁有超過半數的節點數量
2) 注意對節點的備份,必要時可以對節點進行恢複
也可以按照相同配置建立一個全新的從節點,恢複複本集後,系統會自動同步資料。但猜測資料量比較大的情況下,耗時會比較長,所以平時對從節點進行備份,還是有必要。