如下圖片摘自MongoDB官方文檔:http://docs.mongodb.org/manual/core/replication-introduction/web
Primary節點接收客戶端全部的寫操做,整個副本集只會有一個primary節點。MongoDB副本集提供嚴格的一致性。主節點將全部的操做寫入一個叫oplog的capped collection(這個collection的大小通常爲磁盤剩餘空間的5%,不一樣的系統可能不同,詳見http://docs.mongodb.org/manual/core/replica-set-oplog/)中,secondary節點經過複製oplog並執行oplog中的全部操做,由於對oplog的執行是冪等的,因此secondary節點上的數據能夠保持和primary節點同樣,固然這有一個「追趕」(catch up)的過程,會存在必定的落後(Lag)有時候由於網絡延遲或宕機致使從節點永遠趕不上主節點,這時候須要採起人爲的干預了(後面會說到Resyncing Member of Replica Set)。mongodb
默認全部的讀操做也是走的primary節點,固然客戶端能夠選擇從secondary節點進行讀取操做以減少主節點的壓力(後面會對讀寫分離有詳細說明)。shell
各個節點之間是經過心跳機制來維持聯繫的,當主節點沒法和集羣中其餘節點通訊超過10秒,集羣會從剩下的節點中選擇一個secondary做爲primary,這個過程叫作選舉(election),每一個secondary節點都有一個優先級priority來參與投票(也能夠有沒有投票權的secondary節點),priority值越大就越優先成爲主節點(全部的節點能夠有相同的優先級,默認值都是1)。election的策略不只僅就是根據priority值來,會綜合不少其餘的因素。總之MongoDB經過heartbeat和election機制實現了自動的Failover:數據庫
副本集要求參與選舉投票(vote)的節點數爲奇數,這很容易理解。當咱們實際環境中由於機器等緣由限制只有兩個(或偶數)的節點,這時爲了實現Automatic Failover引入另外一類節點:仲裁者(arbiter),仲裁者只參與投票不擁有實際的數據,所以它對物理資源要求不嚴格。數組
上面已經提到了primary,secondary和arbiter,整個MongoDB副本集羣中除了這三種類型的節點還有其餘幾種:網絡
官方建議的最小化的副本集爲Three Member Sets,一個primary和兩個secondary。咱們先就搭建一個這樣的測試環境。app
首先創建三個數據目錄和日誌目錄:測試
1. cd /usr/local/mongodb-2.4.1/data/ 2. mkdir -p rs0-0 rs0-1 rs0-2 3. cd /usr/local/mongodb-2.4.1/log/ 4. mkdir -p rs0-0 rs0-1 rs0-2
而後咱們以守護進程的方式啓動三個mongod進程,端口分別是37017,37018和37019:spa
1. ./bin/mongod --fork --dbpath data/rs0-0/ --logpath log/rs0-0/rs0-0.log --rest --replSet rs0 --port 37017 2. ./bin/mongod --fork --dbpath data/rs0-1/ --logpath log/rs0-1/rs0-1.log --rest --replSet rs0 --port 37018 3. ./bin/mongod --fork --dbpath data/rs0-2/ --logpath log/rs0-2/rs0-2.log --rest --replSet rs0 --port 37019
跟啓普通的mongod進程基本相同,不一樣的跟了--replSet選項,rs0是該副本集的名稱。--rest參數是打開web監控頁面,好比咱們這裏監聽37017端口,則打開http://192.168.129.129:38017/(mongod端口加上1000)就能夠看到這個mongodb數據庫進程的信息,若是是副本集就能查看整個副本集的相關信息。rest
而後咱們用mongo shell連上端口爲37017的mongod:
1. ./bin/mongo -port 37017 2. use admin
接着咱們須要初始化一個Replica Set:首先建立一個副本集配置對象:
1. rsconf={ 2. "_id" : "rs0", 3. "members" : [ 4. { 5. "_id" : 0, 6. "host" : "192.168.129.129:37017" 7. } 8. ] 9. }
而後用rs.initiate()進程初始化:
1. rs.initiate(rsconf) 2. { 3. "info" : "Config now saved locally. Should come online in about a minute.", 4. "ok" : 1 5. }
添加成員:
經過rs.add()將另外兩個mongod添加到副本集當中:
1. rs0:PRIMARY> rs.add("192.168.129.129:37018") 2. { "ok" : 1 } 3. rs0:PRIMARY> rs.add("192.168.129.129:37019") 4. { "ok" : 1 }
會發現37017這個mongod默認就是PRIMARY節點了。經過rs.conf()能夠查看集羣的配置狀況:
1. rs0:PRIMARY> rs.conf() 2. { 3. "_id" : "rs0", 4. "version" : 3, 5. "members" : [ 6. { 7. "_id" : 0, 8. "host" : "192.168.129.129:37017" 9. }, 10. { 11. "_id" : 1, 12. "host" : "192.168.129.129:37018" 13. }, 14. { 15. "_id" : 2, 16. "host" : "192.168.129.129:37019" 17. } 18. ] 19. }
修改priority:
副本中全部的secondary節點都有一個priority值,爲任意的浮點數,該值越大則該節點在election中越優先成爲primary節點,經過下面的命令修改該值,目前primary節點是37017:
1. rs0:PRIMARY> cfg=rs.conf() 2. { 3. "_id" : "rs0", 4. "version" : 7, 5. "members" : [ 6. { 7. "_id" : 0, 8. "host" : "192.168.129.129:37017" 9. }, 10. { 11. "_id" : 1, 12. "host" : "192.168.129.129:37018" 13. }, 14. { 15. "_id" : 2, 16. "host" : "192.168.129.129:37019" 17. } 18. ] 19. }
咱們將37019節點的priority設置成2:
1. rs0:PRIMARY> cfg=rs.conf() 2. cfg.members[2].priority = 2 3. 2
這裏數組的索引2其實跟rs.conf查看到的每一個成員的_id不是一回事。
而後執行:
1. rs0:PRIMARY> rs.reconfig(cfg)
注意:執行rs.reconfig()命令會強制整個副本集集羣進行一次election,這樣priority較高的37019節點便成了primary節點:
整個election過程須要一點時間,在這之間整個集羣的全部節點都是secondary。
添加仲裁者:
首先須要啓動一個做爲arbiter的mongod進程,端口40000,雖然arbiter不持有數據可是仍然須要數據目錄來保存一些配置信息:
1. mkdir –p data/rs0-arb 2. mkdir –p log/rs0-arb 3. ./bin/mongod --fork --dbpath data/rs0-arb/ --logpath log/rs0-arb/rs0-arb.log --rest --replSet rs0 --port 40000
而後進入primary節點執行下面命令添加arbiter:
1. rs0:PRIMARY> rs.addArb("192.168.129.129:40000") 2. { "ok" : 1 } 3. rs0:PRIMARY> rs.conf() 4. { 5. "_id" : "rs0", 6. "version" : 6, 7. "members" : [ 8. { 9. "_id" : 0, 10. "host" : "192.168.129.129:37017" 11. }, 12. { 13. "_id" : 1, 14. "host" : "192.168.129.129:37018" 15. }, 16. { 17. "_id" : 2, 18. "host" : "192.168.129.129:37019" 19. }, 20. { 21. "_id" : 3, 22. "host" : "192.168.129.129:40000", 23. "arbiterOnly" : true 24. } 25. ] 26. }
仲裁節點的做用:
經過實際測試發現,當整個副本集集羣中達到50%的節點(包括仲裁節點)不可用的時候,剩下的節點只能成爲secondary節點,整個集羣只能讀不能寫。好比集羣中有1個primary節點,2個secondary節點,加1個arbit節點時:當兩個secondary節點掛掉了,那麼剩下的原來的primary節點也只能降級爲secondary節點;當集羣中有1個primary節點,1個secondary節點和1個arbit節點,這時即便primary節點掛了,剩下的secondary節點也會自動成爲primary節點。由於仲裁節點不復制數據,所以利用仲裁節點能夠實現最少的機器開銷達到兩個節點熱備的效果。
移除成員:
移除一個成員使用rs.remove()命令:
1. rs0:PRIMARY> rs.remove("192.168.129.129:37019") 2. Sun Aug 11 12:19:22.754 DBClientCursor::init call() failed 3. Sun Aug 11 12:19:22.874 JavaScript execution failed: Error: error doing query: failed at src/mongo/shell/query.js:L78 4. Sun Aug 11 12:19:22.909 trying reconnect to 127.0.0.1:37017 5. Sun Aug 11 12:19:22.909 reconnect 127.0.0.1:37017 ok
須要注意的是:雖然有錯誤信息,但其實操做已經成功了。參看官方的文檔:
每改變一次集羣的配置,副本集的version都會加1。咱們從新將37019加入rs0此次提示信息有點不同:
1. rs0:PRIMARY> rs.add("192.168.129.129:37019") 2. { "down" : [ "192.168.129.129:37019" ], "ok" : 1 }
咱們打開http://192.168.129.129:38017/能夠看到整個副本集的相關信息:
至此一個簡單的用於開發和測試Three Member Sets就搭建完成了。下節會在此基礎上作一些簡單的數據測試。