Mongodb Replica Sets 副本集架構實戰(架設、擴充、容災、修復、客戶端代碼連入)

mongodb單機可靠性比較低,我們投入生產環境往往需要多臺服務器的容災和負載均衡,mongodb推薦使用Replica Sets來進行小的容災和負載解決方案。我相信很多中小型互聯網公司3-4臺mongodb服務器的配置無論從抗壓和穩定角度來說都已經足夠了,至少數據量在千萬級以下都不需要考慮sharding分片和cluster集羣。
我們公司最近將mongodb投入生產,本文將對架設,模擬災難(包括斷電,斷網,數據損壞),修復和客戶端(node.js)連接mongodb副本集做一個比較詳細的分享,如有錯誤,歡迎指出,板磚輕拍啊。
[參考資料]
參考:mongodb 官方API 
http://www.mongodb.org/display/DOCS/Replica+Sets
參考:Kristina Chodorow's 博客(mongodb開發程序員的博客)http://www.snailinaturtleneck.com/blog/2012/05/07/replica-set-internals-bootcamp-part-iv-syncing/

參考:
Kristina 寫的3本mongodb書籍 《MongoDB 權威指南》、《Scaling MongoDB》和《50 Tips and Tricks for MongoDB Developers》
參考:noSqlFan上的相關文章 
http://blog.nosqlfan.com/tags/MongoDB


1、架設

我們首先需要3臺mongodb服務器,(除去仲裁服務器,最好是4臺,之後會說明,這裏我是用了mongodb v2.0版本),作爲最基本的mongodb副本集搭建基礎,還要保證這3臺服務器網絡互通。
a\啓動
首先我們分別啓動3臺mongodb,命令如下:
四臺mongodb服務器的ip分別爲:10.1.10.31,  10.1.10.28,  10.1.10.30, 10.1.49.225(仲裁服務器)
mongod --replSet wzh --rest --dbpath /usr/local/wzhdb/ --journal --port 10001
簡單說明一下:
--replSet wzh:表示副本集的名字爲“wzh”,這裏的名字可以任意取;
--rest:是打開web監控頁面,比如我們這裏監聽10001端口,則打開http://10.1.49.225:11001/就可以看到這個mongodb數據庫進程的信息
--journal:打開日誌,我們這裏模擬生產環境,所以建議將日誌打開,以防不測。日誌將會記錄在/usr/local/wzhdb/journal/下,也就是你的數據庫目錄下(仲裁服務器不存儲數據,不用打開此選項)
另外:這裏沒有使用後臺運行,爲了便於查看連接,我儘量模擬生產環境。


b\初始化
我們現在暫時將10.1.10.28作爲master,於是我們用mongo命令登錄10.1.10.28,來初始化副本集。
有3種方式可以初始化一個副本集:
1、db.runCommand( { replSetInitiate : <config_object> } )
2、rs.initiate(<config_object>)
3、rs.initiate()//先初始化,再通過rs.add等方法修改
這裏的config_object會記錄在local.system.replset這個集合內,這個集合會自動的在副本集成員之間廣播,而且我們不能直接修改他們,需要使用命令來改變它,例如(replSetInitiate 命令)。
我們來看下config_object可以包括那些東西,下面是一個config對象的例子:


{
  _id : <setname>,
  members: [
    {
      _id : <ordinal>,
      host : <hostname[:port]>
      [, arbiterOnly : true]
      [, buildIndexes : <bool>]
      [, hidden : true]
      [, priority: <priority>]
      [, tags: {loc1 : desc1, loc2 : desc2, ..., locN : descN}]
      [, slaveDelay : <n>]
      [, votes : <n>]
    }
    , ...
  ],
  [settings: {
    [getLastErrorDefaults: <lasterrdefaults>]
    [, getLastErrorModes : <modes>]
  }]
}

詳細說明:(默認值在括號中)
_id:副本集的名字,必須和命令行的名字匹配,也就是您剛纔啓動mongodb數據庫命令行的那個名字,數字字母,不能包含"/";
members:一個數組用來表示副本集中的每個成員,這個數組必須包含_id和host這2個key。

members 數組:
_id:在副本集中的每一個成員都必須有一個_id表示,這個_id是通常是數字,從0開始增長。需要注意的是當其中一個成員退休了(指從副本集config中移除了),新加入的成員不能重新使用這個退休成員的_id;
host:ip地址和端口號;
arbiterOnly(false)如果是true,則表示這個成員爲仲裁節點,不接收數據;
buildIndexes(true)如果設置爲false,則會阻止在這個節點上創建第二索引,通常這個節點是作爲純粹的數據備份,從不用來被查詢。不過也因爲此節點沒有第二索引,所以他寫入的東西很少,也就需要很少的內存和磁盤。_id的索引還是會被創建的。只有當priority屬性設置爲0時,此項才能設置爲false,一般不會用到這個選項;
hidden(false):如果此項爲true,不要告訴客戶端的此節點的存在,設置隱藏節點的原因是此節點的數據的使用模式和其他節點大爲不同,比如:報表,統計,備份等。設置爲ture時,允許你針對這個節點發送非主要查詢。
priority(1.0):權重,更高的權重會被選舉爲主節點
tags({}):一個文檔代表這臺服務器的位置,有利於位置感知的讀寫。其實就是表示此節點位於哪個數據中心的,mongodb會根據tags找近的數據中心節點同步數據。
slaveDelay(0):同步數據的延遲,設置爲0表示立即更新同步數據。
votes(1):此節點可以發出的投票數,一般不用修改他

settings 對象:settings對象可以在集羣建立起來以後用再進行設置,通常使用默認值

接下來我們來介紹下rs命令:在命令行我們輸入
rs.help()

rs.status()                     { replSetGetStatus : 1 } checks repl set status
rs.initiate()                   { replSetInitiate : null } initiates set with default settings
rs.initiate(cfg)                { replSetInitiate : cfg } initiates set with configuration cfg
rs.conf()                       get the current configuration object from local.system.replset
rs.reconfig(cfg)                updates the configuration of a running replica set with cfg (disconnects)
rs.add(hostportstr)             add a new member to the set with default attributes (disconnects)
rs.add(membercfgobj)            add a new member to the set with extra attributes (disconnects)
rs.addArb(hostportstr)          add a new member which is arbiterOnly:true (disconnects)
rs.stepDown([secs])             step down as primary (momentarily) (disconnects)
rs.freeze(secs)                 make a node ineligible to become primary for the time specified
rs.remove(hostportstr)          remove a host from the replica set (disconnects)
rs.slaveOk()                    shorthand for db.getMongo().setSlaveOk()

db.isMaster()                   check who is primary

命令就不翻譯 了,我們現在建立一份配置文件,然後啓動它,啓動我們的mongodb副本集:

vavar conf = {
 _id : "wzh",
 members: [
   {
     _id : 0,
     host : "10.1.10.28:10001"
   },
   {
     _id : 1,
      host : "10.1.10.30:10001"  
   },
   {
     _id : 2,
      host : "10.1.10.31:10001"  
   },
   {
     _id : 3,
     host : "10.1.49.225:10001",
     arbiterOnly:true  
   }
 ]
};rs.initiate(conf);

執行後出現:

{
       "info" : "Config now saved locally.  Should come online in about a minute.",
       "ok" : 1
}

等1分鐘整個副本集就正常啓動起來了。
我們打開後臺的副本集控制檯看下:

Mongodb Replica Sets 副本集實戰(架設、擴充、容災、修復、客戶端代碼連入) - snoopyxdy - snoopyxdy的博客

28是主,30和31是從,而我的虛擬機225則作爲仲裁服務器在這個副本集中。

2、測試副本集同步
我們的副本集已經正常啓動起來了,我們來測試一下副本集啓動的情況吧,我們直接連上28,發現命令控制行的前綴變成 了:
PRIMARY>


var tags = ["abc","bcd","efg","fgh","ooo","jjj","kkk","lll","mmm"];

for(var i=0;i<100000;i++){
db.test.insert({"name":"groupa","tags":tags})
}

這裏我們往test集合裏插入了10W條數據,爲什麼要帶tags的key呢,我是爲了另外一個篇文章所用的,利用mongodb做分詞檢索
PRIMARY> db.test.find();  //0.25GB的數據
PRIMARY> db.test.count() //100000
主服務器的10W條記錄已經成功插入了,我們看下另外2臺secondary的節點
分別連上30和31服務器
運行
SECONDARY> db.getMongo().setSlaveOk();
SECONDARY> db.test.count()//100000
發現數據已經完全同步過來了

3、刪除和增加
如果相對副本集進行擴容,想加入一臺mongodb服務器進入副本集,我們首先需要啓動這個節點,最好是將數據事先拷貝一份啓動,不然一個新的空的數據庫進來同步可能會複製過多的數據而導致應用奔潰。
我們登錄28服務器,先將30服務器去掉。

PRIMARY> rs.remove("10.1.10.30:10001");


Mongodb Replica Sets 副本集實戰(架設、擴充、容災、修復、客戶端代碼連入) - snoopyxdy - snoopyxdy的博客

 
觀察副本集的狀態,發現只剩下3臺了,於是我們再往28服務器插入1000條數據


var tags = ["nn","nn","vv","yy","ii","kk","gg","ee","aa"];

for(var i=0;i<1000;i++){
db.test.insert({"name":"groupa","tags":tags})
}

1000條數據是瞬間插入完成的。這樣28和剩下的31服務器上就有了10W1000條數據了。
接下來我們把30服務器加回去,登錄28服務器:


PRIMARY> rs.add({_id:4,host:"10.1.10.30:10001"});
{ "ok" : 1 }

將30服務器加回去,啓動30上的mongodb;我們看下新加的1000條數據有沒有同步過去了。登錄30服務器


SECONDARY> SECONDARY> db.getMongo().setSlaveOk();
SECONDARY> db.test.count();
101000

就是這麼簡單,新加入的節點已經可以同步數據和正常工作了。


2、容災

1、單個節點意外崩潰
現在副本集中28是主,30和31是從,所以我手動將28的mongodb進程kill掉,看看整個集羣是否還可以正常運作。
大約幾秒鐘,mongodb仲裁服務器選舉出了一位新主人:

Mongodb Replica Sets 副本集實戰(架設、擴充、容災、修復、客戶端代碼連入) - snoopyxdy - snoopyxdy的博客

 然後我們往新的主節點30插入1000條數據。


PRIMARY>
PRIMARY> var tags = ["nn","nn","vv","yy","ii","kk","gg","ee","aa"];for(var i=0;i<1000;i++){
... db.test.insert({"name":"groupa","tags":tags})
... }
PRIMARY>
PRIMARY> db.test.count()
102000

現在我們一共10W2000條數據了,很快模擬故障修復了,我們正常啓動28節點了,很明顯30節點當老大很爽,不願意將老大的位置再讓回給28了:

Mongodb Replica Sets 副本集實戰(架設、擴充、容災、修復、客戶端代碼連入) - snoopyxdy - snoopyxdy的博客

 經過查詢,發現28節點的數據也已經是10W2千條了,在28down的過程中,新增加的1000條數據已經同步過去拉。

2、同時意外崩潰2個節點
我們現在同時將副本集內的2個節點kill掉,模擬一個比較大的災難。

Mongodb Replica Sets 副本集實戰(架設、擴充、容災、修復、客戶端代碼連入) - snoopyxdy - snoopyxdy的博客

 
上圖可以看到只剩下31一臺節點苦苦支撐了,另外2個兄弟已經down了,這時副本集中可用的數據節點只有1個了,仲裁服務器無數據的,所以自動降級爲secondary,這時整個集羣只可讀,不可寫。我們嘗試往31數據庫插入一些數據.


SECONDARY> var tags = ["nn","nn","vv","yy","ii","kk","gg","ee","aa"];for(var i=0;i<1000;i++){
... db.test.insert({"name":"groupa","tags":tags})
... }
not master

曝出了不是master的錯誤,之前在secondary上也是無法進行寫操作的。之後修復問題或者網絡,重新啓動28和31的進程,就又能回覆正常了。

3、衰到家了,其中一臺硬盤數據損壞
我們現在模擬硬盤數據丟失的情況,比如我們的應用已經跑了一段時間了,突然30節點down機了,發現硬盤數據損壞了,我們的mongodb數據也全部丟失了,幸好我們當時建立了集羣有其他兄弟備份着。
我們對30節點換了一塊新硬盤,裝好了系統以後準備加入這個集羣,但是,且慢!
我們可以讓集羣中的一臺服務器比如31,先脫離集羣,然後將數據文件拷貝到30上,然後將30和31再加入集羣,這樣就不至於數據相差太大,同步過久導致整個應用緩慢或者崩潰。
這裏就需要我們對副本集設置4臺機器了,1臺崩潰了,1臺去修復了,還有2臺正好1主1從抗住應用。所以如果只有3臺的話,當副本集只剩下1臺節點會進入secondary,就無法寫入操作了,無法正常運行應用了。
總結一下具體步驟:
1、將副本集中的某一臺機器A脫離副本集
2、將這臺A機器的數據庫文件夾copy到新安裝的B機器上,或者啓動mongodb副本集讓這2臺機器慢慢的同步,這樣脫離應用的副本集同步不會拖慢整個副本集
3、同步完成以後分別將A和B機器加入到原來的副本集中即可。



3、客戶端代碼連入mongodb副本集
mongodb副本集主要是爲了容災備份和負載均衡用的。我們一般的互聯網應用大多是讀多寫少,所以mongodb副本集只有一個主,而有多個從。那我們的客戶端代碼如何正確的連入mongodb副本集呢?下面我就以node.js利用rrestjs框架 和 node-mongodb-native 模塊進行mongodb副本集的操作。
rrestjs框架官網:http://www.rrestjs.com/
官方幫助文檔地址:https://github.com/christkv/node-mongodb-native/blob/master/docs/replicaset.md

只需要將rrestjs的配置文件按如下配置即可:
MongodbRC:'wzh',//如果是false表示不使用mongodb的副本集,否則爲字符串,表示副本集的名稱
MongodbRChost:['10.1.10.28:10001','10.1.10.30:10001','10.1.10.31:10001'],//表示mongodb副本集的ip:port數組。

然後正常使用您的應用,插入查詢等操作,rrestjs自動會幫你連入mongodb副本集了

Mongodb Replica Sets 副本集架構實戰(架設、擴充、容災、修復、客戶端代碼連入) - snoopyxdy - snoopyxdy的博客
成功的插入了mongodb副本集數據並返回了剛纔插入的內容。


轉自:http://snoopyxdy.blog.163.com/blog/static/60117440201241694254441/


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章