Percona MongoDB 4 搭建副本集

什麼是副本集:

  • 是一組維護相同數據集的mongod進程
  • 提供冗餘,自動故障轉移和高可用性
  • 提供讀取可伸縮性
  • 內部概念或多或少與MySQL的概念類似
    • PRIMARY概念與MySQL複製中的MASTER大體相同
    • SECONDARY概念與MySQL複製中的SLAVE大體相同。
    • 數據複製是異步的,就像在MySQL中同樣。咱們不肯定輔助設備是否與主設備保持同步。可是在MongoDB中,咱們能夠在寫操做上定義「寫問題」來定義須要什麼類型的確認。例如,咱們能夠定義一個寫入須要確認是否至少有一個輔助節點已經應用了該文檔,或者咱們甚至能夠要求全部輔助節點都須要是最新的。
    • 在讀取oplog的節點之間複製數據,這是一個包含修改和插入文檔的特殊上限集合。這個概念相似於MySQL binlog,但它們的工做方式不一樣。oplog的一個特色是它內部的每一個操做都是冪等的。這意味着即便應用一次或屢次,oplog操做也會產生相同的結果。例如,您不能刪除兩次。若是您再次應用相同的刪除操做,那將是一個無操做。插入和更新也是如此。
    • 因爲oplog是數據庫中的集合,咱們能夠輕鬆地查詢它。oplog的內容是在系統的每一個集合上插入或更新的文檔。因爲oplog是一個上限集合,所以它具備固定且可配置的大小。所以,oplog的大小也表示咱們過去能夠爲事件花多長時間。做爲一個粗略的例子:若是咱們的數據庫天天總共1GB插入,更新或刪除文檔,擁有3GB的oplog意味着咱們能夠存儲或多或少三天的事件。

副本集的工做原理

下圖顯示了應用程序查詢三節點副本集的典型環境。node

Percona MongoDB 4 搭建副本集

在正常操做期間,副本集只有一個節點做爲PRIMARY而全部其餘節點都是SECONDARY。PRIMARY成員是惟一接收寫入的成員。它更新其本地集合和文檔以及oplog。而後,oplog事件經過複製通道發送到全部SECONDARY節點。每一個SECONDARY節點在本地和異步上應用對本地數據和oplog的相同修改。算法

下圖在內部顯示了副本集的工做原理。每一個節點都鏈接到全部其餘節點,而且有一個心跳機制來ping任何其餘節點。心跳具備用於ping節點的可配置時間,默認值爲10秒。sql

Percona MongoDB 4 搭建副本集

若是全部節點都響應心跳確認,則羣集繼續工做。若是其中一個節點崩潰,例如PRIMARY(最壞的狀況),則發生涉及剩餘節點的選舉mongodb

當SECONDARY在配置的超時後沒有收到對心跳的響應時,它會要求進行選舉。仍然存活的節點投票支持新的PRIMARY。選舉階段一般不須要很長時間,選舉算法足夠複雜,讓他們選擇最佳的次要成爲新的主要。讓咱們說這是次要的,與死亡的初級主要是最新的。shell

除了主要崩潰以外,還有一些節點要求進行選舉的狀況:將節點添加到副本集時,在「啓動副本集」期間或在某些維護活動期間。這種選舉不是本文的目的。數據庫

副本集在選舉成功完成以前沒法處理寫入操做,但若是將此類查詢配置爲在輔助節點上運行,則能夠繼續提供讀取查詢(稍後咱們將對此進行討論)。選舉正確完成後,羣集將恢復正常操做。bash

要正常工做,副本集須要具備奇數個成員。在網絡分裂的狀況下,只有奇數個成員確保咱們在其中一個子集中擁有大多數投票。在具備大多數節點的子集中選擇新的PRIMARY。服務器

所以,三個是副本集的最小節點數,以確保高可用性。網絡

仲裁節點

因爲每一個節點都須要擁有完整的數據副本,所以若是您擁有龐大的數據庫,則須要提供至少三臺具備大量磁盤,內存和CPU資源的計算機。這可能很昂貴。app

幸運的是,您能夠將其中一個節點配置爲Arbiter,這是一個不復制數據的特殊成員。它是空的,但它能夠在選舉期間投票。

使用仲裁節點是維持奇數成員的一個很好的解決方案,而不須要花費不少錢來使第三個節點像其餘節點同樣強大。仲裁節點不能被選爲新主節點,由於它沒有數據。

Percona MongoDB 4 搭建副本集

環境

ip 主機名 系統
172.18.11.142 nodejs1 Centos 7.6
172.18.11.143 nodejs2 Centos 7.6
172.18.11.144 nodejs3 Centos 7.6

安裝

# 下載安裝包並安裝
mkdir -p /opt/mongodb/
cat <<EOF > /opt/mongodb/mongodb_down.sh
cd /opt/mongodb/
wget https://www.percona.com/downloads/percona-server-mongodb-LATEST/percona-server-mongodb-4.0.9-4/binary/redhat/7/x86_64/percona-server-mongodb-shell-4.0.9-4.el7.x86_64.rpm
wget https://www.percona.com/downloads/percona-server-mongodb-LATEST/percona-server-mongodb-4.0.9-4/binary/redhat/7/x86_64/percona-server-mongodb-mongos-4.0.9-4.el7.x86_64.rpm
wget https://www.percona.com/downloads/percona-server-mongodb-LATEST/percona-server-mongodb-4.0.9-4/binary/redhat/7/x86_64/percona-server-mongodb-tools-4.0.9-4.el7.x86_64.rpm
wget https://www.percona.com/downloads/percona-server-mongodb-LATEST/percona-server-mongodb-4.0.9-4/binary/redhat/7/x86_64/percona-server-mongodb-server-4.0.9-4.el7.x86_64.rpm
wget https://www.percona.com/downloads/percona-server-mongodb-LATEST/percona-server-mongodb-4.0.9-4/binary/redhat/7/x86_64/percona-server-mongodb-4.0.9-4.el7.x86_64.rpm
EOF

bash -x /opt/mongodb/mongodb_down.sh
yum localinstall /opt/mongodb/*.rpm -y
systemctl enable mongod

主機名

cat <<EOF >> /etc/hosts
192.168.0.249 k8s-m1
192.168.0.250 k8s-n1
192.168.0.251 k8s-n2
EOF

配置文件

cat <<EOF > /etc/mongod.conf
storage:
  dbPath: /var/lib/mongo
  journal:
    enabled: true
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongo/mongod.log
processManagement:
  fork: true
  pidFilePath: /var/run/mongod.pid
net:
  port: 27017
  bindIp: 0.0.0.0
EOF

啓動

systemctl start mongod

副本集的名稱

副本集的名稱這裏使用 rs-smy,將副本集名稱放置於每一臺主機的 /etc/mongod.conf

replication:
     replSetName: "rs-smy"

重啓全部服務器

systemctl restart mongod

初始化集羣

隨便鏈接到一個節點,發出 rs.initiate() 讓副本集直到有哪些成員

rs.initiate( {
_id: "rs-smy",
members: [
{ _id: 0, host: "172.18.11.142:27017" },
{ _id: 1, host: "172.18.11.143:27017" },
{ _id: 2, host: "172.18.11.144:27017" }
] })

發出命令後,MongoDB使用默認配置啓動複製過程。選擇PRIMARY節點,如今將建立的全部文檔將在SECONDARY節點上異步複製。

咱們能夠經過查看mongo shell提示符來驗證複製是否正常。一旦副本集啓動並運行,提示應該在PRIMARY節點上以下:

rs-smy:PRIMARY>

在SECONDARY節點上這樣:

rs-test:SECONDARY>

一些有用的命令

有幾個命令能夠調查並在副本集上執行一些管理任務。這裏有幾個。

要調查副本集配置,您能夠在任何節點上發出rs.conf()

rs-smy:SECONDARY> rs.conf()
{
    "_id" : "rs-smy",
    "version" : 1,
    "protocolVersion" : NumberLong(1),
    "writeConcernMajorityJournalDefault" : true,
    "members" : [
        {
            "_id" : 0,
            "host" : "172.18.11.142:27017",
            "arbiterOnly" : false,
            "buildIndexes" : true,
            "hidden" : false,
            "priority" : 1,
            "tags" : {

            },
            "slaveDelay" : NumberLong(0),
            "votes" : 1
        },
        {
            "_id" : 1,
            "host" : "172.18.11.143:27017",
            "arbiterOnly" : false,
            "buildIndexes" : true,
            "hidden" : false,
            "priority" : 1,
            "tags" : {

            },
            "slaveDelay" : NumberLong(0),
            "votes" : 1
        },
        {
            "_id" : 2,
            "host" : "172.18.11.144:27017",
            "arbiterOnly" : false,
            "buildIndexes" : true,
            "hidden" : false,
            "priority" : 1,
            "tags" : {

            },
            "slaveDelay" : NumberLong(0),
            "votes" : 1
        }
    ],
    "settings" : {
        "chainingAllowed" : true,
        "heartbeatIntervalMillis" : 2000,
        "heartbeatTimeoutSecs" : 10,
        "electionTimeoutMillis" : 10000,
        "catchUpTimeoutMillis" : -1,
        "catchUpTakeoverDelayMillis" : 30000,
        "getLastErrorModes" : {

        },
        "getLastErrorDefaults" : {
            "w" : 1,
            "wtimeout" : 0
        },
        "replicaSetId" : ObjectId("5ce54845de4f73c1e97ea042")
    }
}

咱們能夠看到有關已配置節點的信息,不管是仲裁仍是隱藏,優先級以及有關心跳過程的其餘詳細信息。

要調查副本集狀態,您能夠在任何節點上發出rs.status()

s-smy:SECONDARY> rs.status()
{
    "set" : "rs-smy",
    "date" : ISODate("2019-05-23T10:12:44.663Z"),
    "myState" : 2,
    "term" : NumberLong(7),
    "syncingTo" : "172.18.11.144:27017",
    "syncSourceHost" : "172.18.11.144:27017",
    "syncSourceId" : 2,
    "heartbeatIntervalMillis" : NumberLong(2000),
    "optimes" : {
        "lastCommittedOpTime" : {
            "ts" : Timestamp(1558606360, 1),
            "t" : NumberLong(7)
        },
        "readConcernMajorityOpTime" : {
            "ts" : Timestamp(1558606360, 1),
            "t" : NumberLong(7)
        },
        "appliedOpTime" : {
            "ts" : Timestamp(1558606360, 1),
            "t" : NumberLong(7)
        },
        "durableOpTime" : {
            "ts" : Timestamp(1558606360, 1),
            "t" : NumberLong(7)
        }
    },
    "lastStableCheckpointTimestamp" : Timestamp(1558606350, 1),
    "members" : [
        {
            "_id" : 0,
            "name" : "172.18.11.142:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 24733,
            "optime" : {
                "ts" : Timestamp(1558606360, 1),
                "t" : NumberLong(7)
            },
            "optimeDate" : ISODate("2019-05-23T10:12:40Z"),
            "syncingTo" : "172.18.11.144:27017",
            "syncSourceHost" : "172.18.11.144:27017",
            "syncSourceId" : 2,
            "infoMessage" : "",
            "configVersion" : 1,
            "self" : true,
            "lastHeartbeatMessage" : ""
        },
        {
            "_id" : 1,
            "name" : "172.18.11.143:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 24523,
            "optime" : {
                "ts" : Timestamp(1558606360, 1),
                "t" : NumberLong(7)
            },
            "optimeDurable" : {
                "ts" : Timestamp(1558606360, 1),
                "t" : NumberLong(7)
            },
            "optimeDate" : ISODate("2019-05-23T10:12:40Z"),
            "optimeDurableDate" : ISODate("2019-05-23T10:12:40Z"),
            "lastHeartbeat" : ISODate("2019-05-23T10:12:44.463Z"),
            "lastHeartbeatRecv" : ISODate("2019-05-23T10:12:44.448Z"),
            "pingMs" : NumberLong(0),
            "lastHeartbeatMessage" : "",
            "syncingTo" : "172.18.11.144:27017",
            "syncSourceHost" : "172.18.11.144:27017",
            "syncSourceId" : 2,
            "infoMessage" : "",
            "configVersion" : 1
        },
        {
            "_id" : 2,
            "name" : "172.18.11.144:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 24519,
            "optime" : {
                "ts" : Timestamp(1558606360, 1),
                "t" : NumberLong(7)
            },
            "optimeDurable" : {
                "ts" : Timestamp(1558606360, 1),
                "t" : NumberLong(7)
            },
            "optimeDate" : ISODate("2019-05-23T10:12:40Z"),
            "optimeDurableDate" : ISODate("2019-05-23T10:12:40Z"),
            "lastHeartbeat" : ISODate("2019-05-23T10:12:44.464Z"),
            "lastHeartbeatRecv" : ISODate("2019-05-23T10:12:44.433Z"),
            "pingMs" : NumberLong(0),
            "lastHeartbeatMessage" : "",
            "syncingTo" : "",
            "syncSourceHost" : "",
            "syncSourceId" : -1,
            "infoMessage" : "",
            "electionTime" : Timestamp(1558594808, 1),
            "electionDate" : ISODate("2019-05-23T07:00:08Z"),
            "configVersion" : 1
        }
    ],
    "ok" : 1,
    "operationTime" : Timestamp(1558606360, 1),
    "$clusterTime" : {
        "clusterTime" : Timestamp(1558606360, 1),
        "signature" : {
            "hash" : BinData(0,"eo6zfAdAzzCTw/rQj+OWbd7Vots="),
            "keyId" : NumberLong("6693835933885661186")
        }
    }
}

能夠看到哪一個是PRIMARY,哪一個是SECONDARY

測試複製

鏈接到PRIMARY節點並建立例子:

rs-smy:PRIMARY> use test
switched to db test
rs-test:PRIMARY> db.foo.insert( {name:"Bruce", surname:"Dickinson"} )
WriteResult({ "nInserted" : 1 })
rs-smy:PRIMARY> db.foo.find().pretty()
{
    "_id" : ObjectId("5ae05ac27e6680071caf94b7")
    "name" : "Bruce"
    "surname" : "Dickinson"
}

而後鏈接到SECONDARY節點並查找相同的文檔。

請記住,您沒法鏈接到SECONDARY節點以讀取數據。默認狀況下,只容許在PRIMARY上進行讀寫操做。所以,若是要讀取SECONDARY節點上的數據,首先須要發出rs.slaveOK()命令。若是您不這樣作,您將收到錯誤。

rs-test:SECONDARY> rs.slaveOK()
rs-test:SECONDARY> show collections
local
<strong>foo</strong>
rs-test:SECONDARY> db.foo.find().pretty()
{
     "_id" : ObjectId("5ae05ac27e6680071caf94b7")
     "name" : "Bruce"
     "surname" : "Dickinson"
}

SECONDARY節點已經複製了集合foo和插入文檔的建立。

相關文章
相關標籤/搜索