MongoDB基礎可參考https://blog.51cto.com/kaliarch/2044423linux
1、概述算法
1.1 MongoDB副本集mongodb
通俗來說,mongodb的副本集至關於具備自動故障恢復的主從集羣,主從集羣和副本集最明顯的特徵爲副本集沒有固定的「主節點」,整個集羣會經過必定的算法選舉出主節點,目前MongoDB官方已經不建議使用主從模式了,在主從模式下,若是主數據庫宕機,從數據庫沒法自動接管主數據庫,從而沒法接入數據,取而代之的就是MongoDB副本集模式,主服務器負責整個副本集的讀寫,副本集按期同步數據備份,副本集中的副本節點在主節點掛掉後經過心跳機制檢測到後副本節點就會選舉一個新的主服務器,這一切對於應用服務器無需關心。數據庫
1.2 架構圖bash
1.3 複製原理服務器
mongodb的複製至少須要兩個節點。其中一個是主節點,負責處理客戶端請求,其他的都是從節點,負責複製主節點上的數據。架構
mongodb各個節點常見的搭配方式爲:一主一從、一主多從。app
主節點記錄在其上的全部操做oplog,從節點按期輪詢主節點獲取這些操做,而後對本身的數據副本執行這些操做,從而保證從節點的數據與主節點一致。異步
1.4 副本集特徵:
ide
N 個節點的集羣
任何節點可做爲主節點
全部寫入操做都在主節點上
自動故障轉移
自動恢復
1.5 Bully算法
若是副本集中主節點宕掉後,須要使用bully算法進行選舉主節點,其主要思想爲每一個成員都可以聲明本身爲主節點並通知其餘節點,別的節點能夠選擇接受這個聲明或是拒絕並進入主節點競爭,只有被其餘節點接受的節點才能夠當主節點,
節點按照一些屬性來判斷誰應該勝出。這個屬性能夠是一個靜態ID,也能夠是更新的度量像最近一次事務ID(最新的節點會勝出)
官方描述:
獲得每一個服務器節點的最後操做時間戳。每一個mongodb都有oplog機制會記錄本機的操做,方便和主服務器進行對比數據是否同步還能夠用於錯誤恢復。
若是集羣中大部分服務器down機了,保留活着的節點都爲 secondary狀態並中止,不選舉了。
若是集羣中選舉出來的主節點或者全部從節點最後一次同步時間看起來很舊了,中止選舉等待人來操做。
若是上面都沒有問題就選擇最後操做時間戳最新(保證數據是最新的)的服務器節點做爲主節點。
1.6 Replica Set成員
一個Replica Set中的成員角色有三種:Primary,Secondary和Arbiter。
Primary:接收來自客戶端的全部的寫操做,一個Replica Set中有且只有一個Primary。Primary若是宕掉,Replica Set會自動選舉一個Secondary成爲Primary。Primary將它data sets的全部操做都記錄到oplog中。
Secondary:Secondary從Primary複製oplog,而後將oplog中的操做應用到本身的data sets。Secondary和Primary之間是異步複製,也就是Secondary中的數據可能不是最新的。默認狀況下,Secondary不可讀不可寫,可是能夠經過設置運行客戶端從Secondary讀。
Secondary配置的三種用途:
1.在選舉中阻止其成爲Primary,只用做備份數據。經過設置優先級priority爲0來實現
2.阻止應用程序從它讀,經過設置優先級priority爲0和設置hidden爲true來實現。(一個隱藏的成員一樣複製Primary的數據,可是對於客戶端應用程序來說,它不可見。)
3.保留歷史鏡像數據用於數據回檔,好比若是誤刪除數據,可使用Delayed Replica Set成員中的數據恢復。
Arbiter:Arbiter不須要維護本身的data sets,只是當Primary掛掉以後參與投票選擇哪一個Secondary能夠升級爲Primary
Replica Set中的成員個數爲偶數個時,就須要添加一個Arbiter用於投票選舉哪一個能夠升級爲Primary,不能在Primary或者Secondary主機上運行Arbiter
一個Replica Set能夠最多擁有12個成員,可是隻有7個成員能夠同時參與投票選舉成爲Primary,若是成員數量超過12,就須要使用Master-Slave主從複製方式。
部署一個Replica Set至少須要三個成員,一個Arbiter,一個Secondary和一個Primary或者一個Primary,兩個Secondary。
2、搭建部署
2.1 基礎環境
主機名 |
IP地址 |
系統 |
mongodb-1 |
172.20.6.10 | CentOS release 6.9 |
mongodb-2 | 172.20.6.11 | CentOS release 6.9 |
mongodb-3 | 172.20.6.10 | CentOS release 6.9 |
2.2 軟件安裝
在三臺服務器上依次安裝mongodb
wget -c https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel62-3.4.10.tgz tar -zxvf mongodb-linux-x86_64-rhel62-3.4.10.tgz ln -sv mongodb-linux-x86_64-rhel62-3.4.10 mongodb mkdir /usr/local/mongodb/{conf,mongoData,mongoLog} touch /usr/local/mongodb/mongoLog/mongodb.log echo "export PATH=$PAHT:/usr/local/mongodb/bin">/etc/profile.d/mongodb.sh source etc/profile.d/mongodb.sh
定義配置文件
cat >/usr/local/mongodb/conf/mongodb.conf<<EOF dbpath=/usr/local/mongodb/mongoData logpath=/usr/local/mongodb/mongoLog/mongodb.log logappend=true journal=true quiet=true port=27017 replSet=RS #副本集名稱 maxConns=20000 httpinterface=true fork=true EOF
依次啓動三個mongodb
mongodb -f /usr/local/mongodb/conf/mongodb.conf
2.3 副本集部署
挑選任意一臺mongodb進行登陸
use admin #切換到admin數據庫 config = {_id:"RS",members:[ #定義副本集配置 {_id:0,host:"172.20.6.10:27017"}, {_id:1,host:"172.20.6.11:27017"}, {_id:2,host:"172.20.6.12:27017"},] } rs.initiate(config); #初始化副本集配置
RS:PRIMARY> rs.status(); #查看副本集狀態 { "set" : "RS", "date" : ISODate("2017-11-26T14:09:00.054Z"), "myState" : 1, "term" : NumberLong(1), "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1511705333, 1), "t" : NumberLong(1) }, "appliedOpTime" : { "ts" : Timestamp(1511705333, 1), "t" : NumberLong(1) }, "durableOpTime" : { "ts" : Timestamp(1511705333, 1), "t" : NumberLong(1) } }, "members" : [ { "_id" : 0, "name" : "172.20.6.10:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", #主節點 "uptime" : 377, "optime" : { "ts" : Timestamp(1511705333, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2017-11-26T14:08:53Z"), "infoMessage" : "could not find member to sync from", "electionTime" : Timestamp(1511705241, 1), "electionDate" : ISODate("2017-11-26T14:07:21Z"), "configVersion" : 1, "self" : true }, { "_id" : 1, "name" : "172.20.6.11:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", #secondary節點 "uptime" : 109, "optime" : { "ts" : Timestamp(1511705333, 1), "t" : NumberLong(1) }, "optimeDurable" : { "ts" : Timestamp(1511705333, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2017-11-26T14:08:53Z"), "optimeDurableDate" : ISODate("2017-11-26T14:08:53Z"), "lastHeartbeat" : ISODate("2017-11-26T14:09:00.053Z"), "lastHeartbeatRecv" : ISODate("2017-11-26T14:08:59.072Z"), "pingMs" : NumberLong(0), "syncingTo" : "172.20.6.10:27017", "configVersion" : 1 }, { "_id" : 2, "name" : "172.20.6.12:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", #secondary節點 "uptime" : 109, "optime" : { "ts" : Timestamp(1511705333, 1), "t" : NumberLong(1) }, "optimeDurable" : { "ts" : Timestamp(1511705333, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2017-11-26T14:08:53Z"), "optimeDurableDate" : ISODate("2017-11-26T14:08:53Z"), "lastHeartbeat" : ISODate("2017-11-26T14:09:00.053Z"), "lastHeartbeatRecv" : ISODate("2017-11-26T14:08:59.054Z"), "pingMs" : NumberLong(0), "syncingTo" : "172.20.6.10:27017", "configVersion" : 1 } ], "ok" : 1 }
此時replica set集羣已結搭建成功
3、副本集測試
3.1 數據複製測試
在主節點建立數據庫,並建立集合,插入文檔,在secondary查看文檔
此時已經完成在主節點建立數據,接下來在secondary查看數據是否已經同步過去。
mongodb默認是從主節點讀寫數據的,副本節點上不容許讀,須要設置副本節點能夠讀。
db.getMongo().setSlaveOk(); #設置副本節點可讀
此時咱們能夠測試獲得數據,數據已經同步到secondary上,可是沒法在secondary上進行數據的增刪改操做。
3.2 故障轉移測試
目前mongodb-1爲主節點,mongdb-二、mongodb-3爲副本集節點,此時停掉主節點的mongod服務,進行故障轉移測試。
宕掉主節點mongodb-1的服務後,咱們登陸mongodb-2,查看副本集狀態:
RS:PRIMARY> rs.status() { "set" : "RS", "date" : ISODate("2017-11-26T14:35:03.422Z"), "myState" : 1, "term" : NumberLong(2), "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1511706901, 1), "t" : NumberLong(2) }, "appliedOpTime" : { "ts" : Timestamp(1511706901, 1), "t" : NumberLong(2) }, "durableOpTime" : { "ts" : Timestamp(1511706901, 1), "t" : NumberLong(2) } }, "members" : [ { "_id" : 0, "name" : "172.20.6.10:27017", "health" : 0, "state" : 8, "stateStr" : "(not reachable/healthy)", #mongodb-1已經失去鏈接 "uptime" : 0, "optime" : { "ts" : Timestamp(0, 0), "t" : NumberLong(-1) }, "optimeDurable" : { "ts" : Timestamp(0, 0), "t" : NumberLong(-1) }, "optimeDate" : ISODate("1970-01-01T00:00:00Z"), "optimeDurableDate" : ISODate("1970-01-01T00:00:00Z"), "lastHeartbeat" : ISODate("2017-11-26T14:35:02.502Z"), "lastHeartbeatRecv" : ISODate("2017-11-26T14:32:20.434Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "Connection refused", "configVersion" : -1 }, { "_id" : 1, "name" : "172.20.6.11:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", #mongodb-2爲新的主節點 "uptime" : 1842, "optime" : { "ts" : Timestamp(1511706901, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2017-11-26T14:35:01Z"), "electionTime" : Timestamp(1511706750, 1), "electionDate" : ISODate("2017-11-26T14:32:30Z"), "configVersion" : 1, "self" : true }, { "_id" : 2, "name" : "172.20.6.12:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", #mongodb-3爲secondary節點 "uptime" : 1671, "optime" : { "ts" : Timestamp(1511706901, 1), "t" : NumberLong(2) }, "optimeDurable" : { "ts" : Timestamp(1511706901, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2017-11-26T14:35:01Z"), "optimeDurableDate" : ISODate("2017-11-26T14:35:01Z"), "lastHeartbeat" : ISODate("2017-11-26T14:35:02.354Z"), "lastHeartbeatRecv" : ISODate("2017-11-26T14:35:02.730Z"), "pingMs" : NumberLong(0), "syncingTo" : "172.20.6.11:27017", "configVersion" : 1 } ], "ok" : 1 }
查看mongodb-2的日誌,發現mongodb-1心跳檢查已經失去鏈接,從新進行了主節點選舉
此時在新節點mongodb-2進行文檔插入操做
此時上線mongodb-1,查看集羣狀態與數據是否正常同步到mongodb-1上。
啓動mongodb-1的服務,查看集羣狀態,此時mongodb-1已結成爲新的secondary節點。
RS:PRIMARY> rs.status() { "set" : "RS", "date" : ISODate("2017-11-27T02:13:41.683Z"), "myState" : 1, "term" : NumberLong(2), "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1511748812, 1), "t" : NumberLong(2) }, "appliedOpTime" : { "ts" : Timestamp(1511748812, 1), "t" : NumberLong(2) }, "durableOpTime" : { "ts" : Timestamp(1511748812, 1), "t" : NumberLong(2) } }, "members" : [ { "_id" : 0, "name" : "172.20.6.10:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", #mongodb-1爲secondary節點 "uptime" : 1945, "optime" : { "ts" : Timestamp(1511748812, 1), "t" : NumberLong(2) }, "optimeDurable" : { "ts" : Timestamp(1511748812, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2017-11-27T02:13:32Z"), "optimeDurableDate" : ISODate("2017-11-27T02:13:32Z"), "lastHeartbeat" : ISODate("2017-11-27T02:13:41.373Z"), "lastHeartbeatRecv" : ISODate("2017-11-27T02:13:40.854Z"), "pingMs" : NumberLong(0), "syncingTo" : "172.20.6.12:27017", "configVersion" : 1 }, { "_id" : 1, "name" : "172.20.6.11:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", #mongodb-2爲主節點 "uptime" : 43760, "optime" : { "ts" : Timestamp(1511748812, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2017-11-27T02:13:32Z"), "electionTime" : Timestamp(1511706750, 1), "electionDate" : ISODate("2017-11-26T14:32:30Z"), "configVersion" : 1, "self" : true }, { "_id" : 2, "name" : "172.20.6.12:27017", #mongodb-3爲secondary節點 "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 43589, "optime" : { "ts" : Timestamp(1511748812, 1), "t" : NumberLong(2) }, "optimeDurable" : { "ts" : Timestamp(1511748812, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2017-11-27T02:13:32Z"), "optimeDurableDate" : ISODate("2017-11-27T02:13:32Z"), "lastHeartbeat" : ISODate("2017-11-27T02:13:41.220Z"), "lastHeartbeatRecv" : ISODate("2017-11-27T02:13:41.209Z"), "pingMs" : NumberLong(0), "syncingTo" : "172.20.6.11:27017", "configVersion" : 1 } ], "ok" : 1 }
查看mongodb-1數據已經正常同步。
4、其餘
若是考慮到主服務器的複製壓力過大,能夠製做仲裁節點,其中的仲裁節點不存儲數據,只是負責故障轉移的羣體投票,這樣就少了數據複製的壓力。
刪除節點:
rs.remove("172.20.6.12:27017") #刪除節點
添加節點
rs.add("172.20.6.12:27017") #添加節點 rs.addArb("172.20.6.12:27017") #添加arbiter節點
{ "_id" : 2, "name" : "172.20.6.12:27017", "health" : 1, "state" : 7, "stateStr" : "ARBITER", #arbiter節點 "uptime" : 4, "lastHeartbeat" : ISODate("2017-11-27T02:35:01.634Z"), "lastHeartbeatRecv" : ISODate("2017-11-27T02:35:00.637Z"), "pingMs" : NumberLong(0), "syncingTo" : "172.20.6.11:27017", "configVersion" : 9 }