MongoDB副本集的原理,搭建

介紹:

mongodb副本集即客戶端鏈接到整個副本集,不關心具體哪一臺機器是否掛掉。主服務器負責整個副本集的讀寫,副本集按期同步數據備份,一旦主節點掛掉,副本節點就會選舉一個新的主服務器,這一切對於應用服務器不須要關心
副本集中的副本節點在主節點掛掉後經過心跳機制檢測到後,就會在集羣內發起主節點的選舉機制,自動選舉一位新的主服務器
選舉還有個前提條件,參與選舉的節點數量必須大於副本集總節點數量的一半,若是已經小於一半了全部節點保持只讀狀態。所以,官方推薦咱們的副本集機器數量至少爲3個:[一個主節點,兩個副本節點] 也能夠爲[一個主節點,一個副本節點,一個仲裁節點]
其中有一個仲裁節點,即當參與選舉的節點沒法選出主節點時仲裁節點充當仲裁的做用。仲裁節點不存儲數據,只是仲裁。因此,咱們的副本集能夠設置爲:1主+1從+1仲裁

副本同步主的數據的過程:

Primary節點寫入數據,Secondary經過讀取Primary的oplog獲得複製信息,開始複製數據而且將複製信息寫入到本身的oplog。若是某個操做失敗,則備份節點中止從當前數據源複製數據。若是某個備份節點因爲某些緣由掛掉了,當從新啓動後,就會自動從oplog的最後一個操做開始同步,同步完成後,將信息寫入本身的oplog,因爲複製操做是先複製數據,複製完成後再寫入oplog,有可能相同的操做會同步兩份,不過MongoDB在設計之初就考慮到這個問題,將oplog的同一個操做執行屢次,與執行一次的效果是同樣的。簡單的說就是:python

當Primary節點完成數據操做後,Secondary會作出一系列的動做保證數據的同步:
1:檢查本身local庫的oplog.rs集合找出最近的時間戳。
2:檢查Primary節點local庫oplog.rs集合,找出大於此時間戳的記錄。
3:將找到的記錄插入到本身的oplog.rs集合中,並執行這些操做 
linux

副本集的同步和主從同步同樣,都是異步同步的過程,不一樣的是副本集有個自動故障轉移的功能。其原理是:slave端從primary端獲取日誌,而後在本身身上徹底順序的執行日誌所記錄的各類操做(該日誌是不記錄查詢操做的),這個日誌就是local數據 庫中的oplog.rs表,默認在64位機器上這個表是比較大的,佔磁盤大小的5%,oplog.rs的大小能夠在啓動參數中設 定:--oplogSize 1000,單位是M。mongodb

注意:在副本集的環境中,要是全部的Secondary都宕機了,只剩下Primary。最後Primary會變成Secondary,不能提供服務數據庫

搭建

環境說明vim

IP 角色
172.16.1.216 primary(主節點)
172.16.1.223 secondary(副本節點)
172.16.1.215 arbiterOnly(仲裁節點)

 

 

 

 

 

三臺機器解壓mongodb安裝包服務器

# curl -O https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-amazon-3.4.6.tgz
# tar -zxvf mongodb-linux-x86_64-amazon-3.4.6.tgz
# mv mongodb-linux-x86_64-amazon-3.4.6/ /usr/local/mongodb

分別在三臺機器上建立mongoDB的data,log以及配置目錄app

# cd /usr/local/mongodb
# mkdir -p data/db
# mkdir log
# touch log/mongod.log
# mkdir etc
# touch etc/mongod.conf

分別在三臺機器上配置config文件curl

vim /usr/local/mongodb/etc/mongod.conf

dbpath = /usr/local/mongodb/data/db        # 指定數據庫路徑
logpath = /usr/local/mongodb/log/mongod.log          # 指定mongodb日誌文件
logappend = true        # 使用追加的方式寫日誌
port = 27017               #設置端口號爲27017
fork = true                   #設置以守護進程的方式啓動mongod
replSet = timecash            #設置副本集的名字爲myrs,同一副本集羣的replSet名稱必需相同

分別在三臺機器上啓動副本集異步

./usr/local/mongodb/bin/mongod -f /usr/local/mongodb/etc/mongod.conf    #指定以mongod.conf配置啓動mongod

初始化副本集測試

登陸任意一臺機器的mongodb執行

./usr/local/mongodb/bin/mongo
>>>use admin
>>>config = {
"_id":"myrs",
"members":[
  {"_id":0,"host":"172.16.1.216:27017"},
  {"_id":1,"host":"172.16.1.223:27017"},
  {"_id":2,"host":"172.16.1.215:27017",arbiterOnly:true} #這個節點就是仲裁節點
]
}
>>>rs.initiate(config);   #初始化配置
{ "ok" : 1 }

查看集羣節點

>>>rs.status();
{
    "set" : "timecash",
    "date" : ISODate("2018-03-08T08:29:32.268Z"),
    "myState" : 1,
    "term" : NumberLong(1),
    "heartbeatIntervalMillis" : NumberLong(2000),
    "optimes" : {
        "lastCommittedOpTime" : {
            "ts" : Timestamp(1520497764, 1),
            "t" : NumberLong(1)
        },
        "appliedOpTime" : {
            "ts" : Timestamp(1520497764, 1),
            "t" : NumberLong(1)
        },
        "durableOpTime" : {
            "ts" : Timestamp(1520497764, 1),
            "t" : NumberLong(1)
        }
    },
    "members" : [
        {
            "_id" : 0,
            "name" : "172.16.1.216:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 788,
            "optime" : {
                "ts" : Timestamp(1520497764, 1),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2018-03-08T08:29:24Z"),
            "electionTime" : Timestamp(1520497194, 1),
            "electionDate" : ISODate("2018-03-08T08:19:54Z"),
            "configVersion" : 1,
            "self" : true
        },
        {
            "_id" : 1,
            "name" : "172.16.1.223:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 591,
            "optime" : {
                "ts" : Timestamp(1520497764, 1),
                "t" : NumberLong(1)
            },
            "optimeDurable" : {
                "ts" : Timestamp(1520497764, 1),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2018-03-08T08:29:24Z"),
            "optimeDurableDate" : ISODate("2018-03-08T08:29:24Z"),
            "lastHeartbeat" : ISODate("2018-03-08T08:29:30.443Z"),
            "lastHeartbeatRecv" : ISODate("2018-03-08T08:29:30.680Z"),
            "pingMs" : NumberLong(0),
            "syncingTo" : "172.16.1.216:27017",
            "configVersion" : 1
        },
        {
            "_id" : 2,
            "name" : "172.16.1.215:27017",
            "health" : 1,
            "state" : 7,
            "stateStr" : "ARBITER",
            "uptime" : 591,
            "lastHeartbeat" : ISODate("2018-03-08T08:29:30.431Z"),
            "lastHeartbeatRecv" : ISODate("2018-03-08T08:29:27.927Z"),
            "pingMs" : NumberLong(0),
            "configVersion" : 1
        }
    ],
    "ok" : 1
}
#能夠看出172.16.1.216是主節點,172.16.1.223是副本節點,172.16.1.215是仲裁節點

測試

1.測試副本集的數據複製功能

登陸172.16.1.216主節點,用mongo登陸到交互模式

./usr/local/mongodb/bin/mongo
timecash:PRIMARY> use test  #建立test數據庫
timecash:PRIMARY> db.testdb.insert({"name":"alex"});       #插入數據

登陸172.16.1.223副本節點,用mongo登陸到交互模式

./usr/local/mongodb/bin/mongo

timecash:SECONDARY>use test
timecash:SECONDARY>show collections
[thread1] Error: listCollections failed: {
"ok" : 0,
"errmsg" : "not master and slaveOk=false",
"code" : 13435,
"codeName" : "NotMasterNoSlaveOk"
}
#能夠看到報錯了 由於mongodb默認是從主節點讀寫數據的,副本節點上不容許讀,須要設置副本節點能夠讀
timecash:SECONDARY>db.getMongo().setSlaveOk();  
#此時副本節點就能夠讀取數據了
timecash:SECONDARY>db.testdb.find();
{ "_id" : ObjectId("59676d711881041abab44477"), "name" : "alex" }

2.測試副本集的故障轉移功能

timecash:PRIMARY> use admin
timecash:PRIMARY> db.shutdownServer()  #停掉 主節點

此時能夠登陸到副本節點查看,副本節點已經升級爲主節點

驗證(Pyhton)

1.主節點斷開,看是否影響寫入

(如今主是216,從是223)

腳本

#!/bin/env python

import time
from pymongo import MongoClient
#兩地址 CONN_ADDR1 = '172.16.1.216:27017' CONN_ADDR2 = '172.16.1.223:27017' REPLICAT_SET = 'timecash' #username = 'demouser' #password = '123456'
#獲取mongoclient client = MongoClient([CONN_ADDR1, CONN_ADDR2], replicaSet=REPLICAT_SET) for i in range(1000): try: client.test.tt.insert({"name":"test" + str(i)}) time.sleep(1) print(client.primary,'主') print(client.secondaries,'從') except Exception as e: print(e)

腳本執行打印的內容

# python3.5 test.py
('172.16.1.216', 27017) 主            #剛開始 216是主,223是從
{('172.16.1.223', 27017)} 從
('172.16.1.216', 27017) 主
{('172.16.1.223', 27017)} 從
('172.16.1.216', 27017) 主
{('172.16.1.223', 27017)} 從
('172.16.1.216', 27017) 主
{('172.16.1.223', 27017)} 從
('172.16.1.216', 27017) 主
{('172.16.1.223', 27017)} 從
('172.16.1.216', 27017) 主
{('172.16.1.223', 27017)} 從
('172.16.1.216', 27017) 主
{('172.16.1.223', 27017)} 從
('172.16.1.216', 27017) 主
{('172.16.1.223', 27017)} 從
('172.16.1.216', 27017) 主
{('172.16.1.223', 27017)} 從
('172.16.1.216', 27017) 主
{('172.16.1.223', 27017)} 從
('172.16.1.216', 27017) 主
{('172.16.1.223', 27017)} 從
('172.16.1.216', 27017) 主
{('172.16.1.223', 27017)} 從
connection closed                        #到這的時候,我把主(216)停了,而後發現寫不進數據了
('172.16.1.223', 27017) 主               #到這的時候,mongo副本集的高可用自動把主切換到223了,216起來的時候 就會變成從
set() 從
('172.16.1.223', 27017) 主
set() 從
('172.16.1.223', 27017) 主
set() 從
('172.16.1.223', 27017) 主
set() 從
('172.16.1.223', 27017) 主
set() 從
('172.16.1.223', 27017) 主
set() 從
('172.16.1.223', 27017) 主
set() 從
('172.16.1.223', 27017) 主
set() 從
('172.16.1.223', 27017) 主
set() 從
('172.16.1.223', 27017) 主
set() 從
('172.16.1.223', 27017) 主
set() 從
('172.16.1.223', 27017) 主
set() 從
('172.16.1.223', 27017) 主
set() 從
('172.16.1.223', 27017) 主
set() 從
('172.16.1.223', 27017) 主
set() 從
('172.16.1.223', 27017) 主
set() 從
('172.16.1.223', 27017) 主
set() 從
('172.16.1.223', 27017) 主
set() 從
('172.16.1.223', 27017) 主
set() 從
('172.16.1.223', 27017) 主
set() 從
('172.16.1.223', 27017) 主             #到這的時候,216起來了 就稱爲從了
{('172.16.1.216', 27017)} 從
('172.16.1.223', 27017) 主
{('172.16.1.216', 27017)} 從
('172.16.1.223', 27017) 主
{('172.16.1.216', 27017)} 從
('172.16.1.223', 27017) 主
{('172.16.1.216', 27017)} 從
('172.16.1.223', 27017) 主
{('172.16.1.216', 27017)} 從

上面是 在執行腳本的時候,模擬主(216)宕機,看到主 遷移到了223上,在把216起來以後 變成 從了.  其中在主(216)掛了以後 切換的過程當中 有一段數據寫入丟了

{ "name" : "GOODODOO15" }
{ "name" : "GOODODOO592" }
{ "name" : "GOODODOO593" }
#其實這部分數據是因爲在選舉過程期間丟失的,要是不容許數據丟失,則把在選舉期間的數據放到隊列中,等到找到新的Primary,再寫入
相關文章
相關標籤/搜索