1. 程式人生 > >搭建高可用MongoDB集群(Replica set)

搭建高可用MongoDB集群(Replica set)

mongodb mongodb副本集 replica set mongodb集群

MongoDB基礎可參考http://blog.51cto.com/kaliarch/2044423

一、概述

1.1 MongoDB副本集

通俗來講,mongodb的副本集相當於具有自動故障恢復的主從集群,主從集群和副本集最明顯的特征為副本集沒有固定的“主節點”,整個集群會通過一定的算法選舉出主節點,目前MongoDB官方已經不建議使用主從模式了,在主從模式下,如果主數據庫宕機,從數據庫無法自動接管主數據庫,從而無法接入數據,取而代之的就是MongoDB副本集模式,主服務器負責整個副本集的讀寫,副本集定期同步數據備份,副本集中的副本節點在主節點掛掉後通過心跳機制檢測到後副本節點就會選舉一個新的主服務器,這一切對於應用服務器無需關心。

1.2 架構圖

技術分享圖片技術分享圖片

技術分享圖片1.3 復制原理

  • mongodb的復制至少需要兩個節點。其中一個是主節點,負責處理客戶端請求,其余的都是從節點,負責復制主節點上的數據。

  • mongodb各個節點常見的搭配方式為:一主一從、一主多從。

  • 主節點記錄在其上的所有操作oplog,從節點定期輪詢主節點獲取這些操作,然後對自己的數據副本執行這些操作,從而保證從節點的數據與主節點一致。

技術分享圖片1.4 副本集特征:

  • N 個節點的集群

  • 任何節點可作為主節點

  • 所有寫入操作都在主節點上

  • 自動故障轉移

  • 自動恢復

1.5 Bully算法

如果副本集中主節點宕掉後,需要使用bully算法進行選舉主節點,其主要思想為每個成員均可以聲明自己為主節點並通知其他節點,別的節點可以選擇接受這個聲明或是拒絕並進入主節點競爭,只有被其他節點接受的節點才可以當主節點,

節點按照一些屬性來判斷誰應該勝出。這個屬性可以是一個靜態ID,也可以是更新的度量像最近一次事務ID(最新的節點會勝出)

官方描述:

  1. 得到每個服務器節點的最後操作時間戳。每個mongodb都有oplog機制會記錄本機的操作,方便和主服務器進行對比數據是否同步還可以用於錯誤恢復。

  2. 如果集群中大部分服務器down機了,保留活著的節點都為 secondary狀態並停止,不選舉了。

  3. 如果集群中選舉出來的主節點或者所有從節點最後一次同步時間看起來很舊了,停止選舉等待人來操作。

  4. 如果上面都沒有問題就選擇最後操作時間戳最新(保證數據是最新的)的服務器節點作為主節點。

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.1 基礎環境

主機名
IP地址
系統
mongodb-1
172.20.6.10CentOS release 6.9
mongodb-2172.20.6.11CentOS release 6.9
mongodb-3172.20.6.10CentOS 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.1 數據復制測試

在主節點創建數據庫,並創建集合,插入文檔,在secondary查看文檔

技術分享圖片

此時已經完成在主節點創建數據,接下來在secondary查看數據是否已經同步過去。

mongodb默認是從主節點讀寫數據的,副本節點上不允許讀,需要設置副本節點可以讀。

db.getMongo().setSlaveOk();            #設置副本節點可讀

技術分享圖片

此時我們可以測試得到數據,數據已經同步到secondary上,但是無法在secondary上進行數據的增刪改操作。

3.2 故障轉移測試

目前mongodb-1為主節點,mongdb-2、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數據已經正常同步。

技術分享圖片

四、其他

如果考慮到主服務器的復制壓力過大,可以制作仲裁節點,其中的仲裁節點不存儲數據,只是負責故障轉移的群體投票,這樣就少了數據復制的壓力。

刪除節點:

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
        }


搭建高可用MongoDB集群(Replica set)