MongoDB 快速入門實(shí)戰(zhàn)教程進(jìn)階篇 二:提高數(shù)據(jù)服務(wù)可用性的復(fù)制集

前一部分的文章:

MongoDB 快速入門實(shí)戰(zhàn)教程基礎(chǔ)篇 一 :文檔的 CR操作
MongoDB 快速入門實(shí)戰(zhàn)教程基礎(chǔ)篇 一 :文檔的 UD操作

MongoDB 快速入門實(shí)戰(zhàn)教程基礎(chǔ)篇 二: 流式聚合操作
MongoDB 快速入門實(shí)戰(zhàn)教程基礎(chǔ)篇 三:執(zhí)行計(jì)劃與索引

MongoDB 快速入門實(shí)戰(zhàn)教程進(jìn)階篇 一:數(shù)據(jù)模型

進(jìn)階篇 二 提高數(shù)據(jù)服務(wù)可用性的復(fù)制集

MongoDB 中的復(fù)制指的是將數(shù)據(jù)同步在多個(gè)服務(wù)器。復(fù)制操作將會(huì)在多個(gè)服務(wù)器上建立數(shù)據(jù)副本,這些副本的集合稱為復(fù)制集,它們存儲(chǔ)的內(nèi)容與主服務(wù)器上的內(nèi)容一致。建立了復(fù)制集之后,就可以在主服務(wù)器出現(xiàn)故障或無(wú)法連接的情況下保證數(shù)據(jù)服務(wù)可用。

復(fù)制集成員

MongoDB 的復(fù)制集可以支持多個(gè)節(jié)點(diǎn),但要求至少有兩個(gè)節(jié)點(diǎn)。任何情況下都只有一個(gè)主節(jié)點(diǎn)(即主服務(wù)器),它負(fù)責(zé)處理客戶端發(fā)出的命令。除主節(jié)點(diǎn)之外的所有節(jié)點(diǎn)都稱為從節(jié)點(diǎn),它們會(huì)主動(dòng)同步主節(jié)點(diǎn)中的數(shù)據(jù)。在 MongoDB 中,主節(jié)點(diǎn)稱為 Primary,從節(jié)點(diǎn)稱為 Secondarie。

主節(jié)點(diǎn) Primary

主節(jié)點(diǎn)是復(fù)制集中唯一一個(gè)可以接收寫操作的成員。MongoDB 在主節(jié)點(diǎn)上執(zhí)行寫操作,并將操作記錄在主節(jié)點(diǎn)的 oplog 中,從節(jié)點(diǎn)會(huì)復(fù)制主節(jié)點(diǎn)的操作記錄,然后執(zhí)行相同操作以實(shí)現(xiàn)數(shù)據(jù)同步的目的。下圖描述了由三個(gè)成員組成的復(fù)制集的成員關(guān)系:
在這里插入圖片描述

這個(gè)復(fù)制集中有兩個(gè)從節(jié)點(diǎn)和一個(gè)主節(jié)點(diǎn)。主節(jié)點(diǎn)接收客戶端發(fā)起的寫操作,從節(jié)點(diǎn)通過(guò) oplog 實(shí)現(xiàn)數(shù)據(jù)同步。要注意的是,復(fù)制集中的所有從節(jié)點(diǎn)都可以接收讀操作,但默認(rèn)情況下讀取操作依然是交給主節(jié)點(diǎn)。如果想要更改讀取規(guī)則,可以查閱官方文檔 Read Preference。要注意的是,每個(gè)復(fù)制集最多可擁有 50 名成員。

從節(jié)點(diǎn) Secondary

從節(jié)點(diǎn)中存儲(chǔ)的是主節(jié)點(diǎn)的數(shù)據(jù)副本。從節(jié)點(diǎn)將主節(jié)點(diǎn)中的 oplog 操作應(yīng)用于自身,從而實(shí)現(xiàn)數(shù)據(jù)同步。要注意的是,這個(gè)“數(shù)據(jù)同步”的操作是異步進(jìn)行的。下圖描述了由三個(gè)成員組成的復(fù)制集的數(shù)據(jù)同步關(guān)系:
在這里插入圖片描述

子節(jié)點(diǎn)會(huì)同步主節(jié)點(diǎn)上的操作,以實(shí)現(xiàn)數(shù)據(jù)同步。另外,各節(jié)點(diǎn)之間通過(guò)心跳(Heartbeat)來(lái)判斷是否可用,假如某個(gè)節(jié)點(diǎn)在 10 秒內(nèi)沒(méi)有相應(yīng)其他節(jié)點(diǎn)發(fā)出的 Heartbeat,那么它將會(huì)被標(biāo)記為“掉線”,意為不可用或不可訪問(wèn)。

復(fù)制的基石—操作日志

上面提到,從節(jié)點(diǎn)的數(shù)據(jù)同步操作其實(shí)是執(zhí)行主節(jié)點(diǎn)中執(zhí)行過(guò)的操作。所有從節(jié)點(diǎn)都會(huì)拷貝主節(jié)點(diǎn)上的 local.oplog.rs 文件,即 oplog。oplog 記錄主節(jié)點(diǎn)中的改動(dòng)操作,但不記錄讀取操作。oplog 是一個(gè)特殊的上限集合,它支持基于順序插入和檢索文檔的高吞吐操作。上限集合的大小是固定的,在達(dá)到最大記錄數(shù)之后,如果再有新的記錄傳入,它會(huì)覆蓋掉最早的記錄。

從 MongoDB4.0 開(kāi)始,我們可以使用 oplogSizeMB 在創(chuàng)建時(shí)設(shè)置 oplog 的大小,或者使用 replSetResizeOplog 使其能夠突破上限集合的限制。假設(shè)我們想要將 oplog 的大小設(shè)置為 16000 兆字節(jié),對(duì)應(yīng)命令如下:

db.adminCommand({replSetResizeOplog: 1, size: 16000})

當(dāng)?shù)谝淮螁⒂脧?fù)制集,且未指定 oplog 大小時(shí),MongoDB 將會(huì)創(chuàng)建一個(gè)默認(rèn)大小的 oplog。oplog 的大小與操作系統(tǒng)和存儲(chǔ)引擎相關(guān),以下默認(rèn)大小規(guī)則適用于類 Unix 操作系統(tǒng)和 Windows 操作系統(tǒng):

存儲(chǔ)引擎 默認(rèn)的 oplog 大小 下限 上限
內(nèi)存存儲(chǔ)引擎 物理內(nèi)存的 5% 50 MB 50 GB
WiredTiger 存儲(chǔ)引擎 可用磁盤空間的 5% 990 MB 50 GB
MMAPv1 存儲(chǔ)引擎 可用磁盤空間的 5% 990 MB 50 GB

對(duì)于 64 位的 macOS,oplog 的大小是 192M 的物理內(nèi)存或磁盤空間,上述三種存儲(chǔ)引擎的默認(rèn)值均相同。

主節(jié)點(diǎn)故障的應(yīng)對(duì)機(jī)制

在主節(jié)點(diǎn)出現(xiàn)故障時(shí),整個(gè)系統(tǒng)應(yīng)該有一個(gè)應(yīng)對(duì)機(jī)制。MongoDB 為復(fù)制集提供了主節(jié)點(diǎn)選舉和數(shù)據(jù)回滾來(lái)確保數(shù)據(jù)服務(wù)可用和避免數(shù)據(jù)丟失。

主節(jié)點(diǎn)選舉

當(dāng)主節(jié)點(diǎn)被標(biāo)記為“掉線”,那么就意味著復(fù)制集需要一個(gè)新的主節(jié)點(diǎn),否則將會(huì)導(dǎo)致服務(wù)不可用。這種情況下,復(fù)制集通過(guò)選舉的方式來(lái)確定哪個(gè)成員會(huì)成為主節(jié)點(diǎn)。除了被標(biāo)記為“掉線”之外,會(huì)觸發(fā)選舉的情況還有以下幾種:

  • 復(fù)制集中添加了新的節(jié)點(diǎn);
  • 初始化復(fù)制集;
  • 使用 rs.stepDown() 或者 rs.reconfig() 等方法維護(hù)復(fù)制集。

下圖描述了因?yàn)橹鞴?jié)點(diǎn)“掉線”引起的選舉:
在這里插入圖片描述

這是一個(gè)由三個(gè)成員組成的復(fù)制集,主節(jié)點(diǎn)“掉線”后,就需要在兩個(gè)從節(jié)點(diǎn)中選出一個(gè)作為新的主節(jié)點(diǎn)。要注意的是,在選舉完成之前,復(fù)制集無(wú)法處理寫操作。在選出新的主節(jié)點(diǎn)之后,復(fù)制集的功能就會(huì)恢復(fù)正常。選舉采用投票制,每個(gè)復(fù)制集最多只能擁有 7 名投票成員,這 7 個(gè)成員最多擁有 1 票。下圖描述了由 9 個(gè)成員組成的復(fù)制集選舉票權(quán):
在這里插入圖片描述

綠色背景的節(jié)點(diǎn)表示節(jié)點(diǎn)可用,灰色背景表示節(jié)點(diǎn)“掉線”。votes 代表票權(quán),對(duì)應(yīng)的數(shù)值代表初始票數(shù)。如果投票成員數(shù)量為偶數(shù),就有可能會(huì)造成多個(gè)節(jié)點(diǎn)的票數(shù)相同,甚至陷入無(wú)限選舉的泥潭。為了避免這種情況,我們就需要增加一個(gè)仲裁(Arbiter)節(jié)點(diǎn)。仲裁節(jié)點(diǎn)擁有投票權(quán),但它沒(méi)有存儲(chǔ)數(shù)據(jù)副本,也不能成為主節(jié)點(diǎn)。新增仲裁節(jié)點(diǎn)后,票數(shù)就會(huì)從偶數(shù)變成奇數(shù)。

默認(rèn)情況下,從標(biāo)記主節(jié)點(diǎn)“掉線”到選舉出新的主節(jié)點(diǎn)的時(shí)間不會(huì)超過(guò) 12 秒,但 MongoDB 也提供了可修改的配置來(lái)調(diào)整該時(shí)間。

數(shù)據(jù)回滾

當(dāng)主節(jié)點(diǎn)發(fā)生故障,并選舉出新的主節(jié)點(diǎn)時(shí),MongoDB 將會(huì)在之前的主節(jié)點(diǎn)上執(zhí)行回滾寫操作。當(dāng)“掉線”的主節(jié)點(diǎn)重新連接時(shí),它將會(huì)以從節(jié)點(diǎn)的身份加入到復(fù)制集中,并回滾寫操作,以便與其他成員的數(shù)據(jù)保持一致。MongoDB 4.0 版本對(duì)數(shù)據(jù)回滾進(jìn)行了一些調(diào)整:

  • 回滾操作會(huì)在后臺(tái)索引構(gòu)建完成后進(jìn)行;
  • 不限制回滾的數(shù)據(jù)量;
  • 回滾時(shí)間默認(rèn)為 24 小時(shí),且可以配置。

4.0 版本之前,回滾的最大數(shù)據(jù)量為 300 兆字節(jié),超過(guò)上限的數(shù)據(jù)量需要進(jìn)行手動(dòng)干預(yù);回滾時(shí)間默認(rèn)為 30 分鐘,且不可配置。

復(fù)制集部署實(shí)踐

我們將介紹由三個(gè)成員組成的復(fù)制集部署過(guò)程,本次部署演示使用 MongoDB 官方的 Docker 鏡像,并且不啟用訪問(wèn)控制。如果想要了解有訪問(wèn)控制的復(fù)制集部署知識(shí),可查閱官方文檔 Deploy New Replica Set With Keyfile Access Control

注意:本次部署演示將在同一臺(tái)計(jì)算機(jī)上啟動(dòng)多個(gè) Docker 鏡像,但實(shí)際工作中則是在多臺(tái)不同的云服務(wù)器上部署 MongoDB。

部署復(fù)制集

在開(kāi)始學(xué)習(xí)本節(jié)之前,請(qǐng)確保按照附章 Docker 官方文檔 的指引安裝 Docker。

首先,我們需要從 DockerHub 中拉取 MongoDB 官方提供的 mongo 服務(wù)鏡像。在版本選擇方面,我們選擇最新版,即 latest。對(duì)應(yīng)命令如下:

$ docker pull mongo:latest

將鏡像拉取到本地后,分別使用 run 命令啟動(dòng)三個(gè)容器。啟動(dòng)時(shí),我們需要為容器指定名稱,以便后期使用,同時(shí)還需要指定復(fù)制集的名稱,并設(shè)置容器的 bind_ip 。對(duì)應(yīng)命令如下:

$ docker run --name mongoFir -d mongo:latest --replSet "mongoRepas" --bind_ip_all

$ docker run --name mongoSec -d mongo:latest --replSet "mongoRepas" --bind_ip_all

$ docker run --name mongoThr -d mongo:latest --replSet "mongoRepas" --bind_ip_all 

這里將容器分別命名為 mongoFir、mongoSecmongoThr,復(fù)制集的名稱指定為 mongoRepas,并解除 mongo 對(duì)于bind_ip 的限制。由于在啟動(dòng)時(shí)未綁定 IP,所以我們需要使用 grep 命令找到每個(gè)容器對(duì)應(yīng)的 IP。對(duì)應(yīng)命令如下:

$ docker inspect mongoFir | grep IPAddress

命令執(zhí)行后,終端返回信息如下:

"SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.2",
                    "IPAddress": "172.17.0.2"

返回結(jié)果說(shuō)明容器 mongoFir 綁定的 IP 為 172.17.0.2,mongo 服務(wù)的默認(rèn)端口為 27017,所以名為 mongoFir 的容器中的 mongo 服務(wù)完整地址為 172.17.0.2:27017。接著依次查找容器 mongoSecmongoThr 對(duì)應(yīng)的 mongo 服務(wù)地址。最終,三個(gè)容器對(duì)應(yīng)的 mongo 服務(wù)地址依次如下:

172.17.0.2:27017
172.17.0.3:27017
172.17.0.4:27017

容器啟動(dòng)成功后,就可以開(kāi)始初始化復(fù)制集的工作了。首先,連接任意一個(gè)容器的 MongoShell,例如容器 mongoFir。對(duì)應(yīng)命令如下:

$ docker exec -it mongoFir mongo

命令執(zhí)行后,就會(huì)連接上容器 mongoFir 的 MongoShell。然后在 MongoShell 中執(zhí)行復(fù)制集初始化的命令:

> rs.initiate({
        _id: "mongoRepas",
        members:[
            {_id: 0, host: "172.17.0.2"},
            {_id: 1, host: "172.17.0.3"},
            {_id: 2, host: "172.17.0.4"}
        ]
})

在初始化復(fù)制集的時(shí)候指定了復(fù)制集的名稱,并制定了成員的 _id 和對(duì)應(yīng)的 IP 地址。命令執(zhí)行后,MongoShell 輸出如下文檔:

{
    "ok" : 1,
    "operationTime" : Timestamp(1564287051, 1),
    "$clusterTime" : {
        "clusterTime" : Timestamp(1564287051, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
}

返回結(jié)果中的 ok: 1 代表復(fù)制集初始化成功。此時(shí),MongoShell 的命令行標(biāo)識(shí)符從 > 變?yōu)?mongoRepas:SECONDARY>,即復(fù)制集的 Shell。在復(fù)制集 Shell 中使用 rs.status() 命令查看當(dāng)前復(fù)制集的狀態(tài)信息,命令執(zhí)行后輸出如下內(nèi)容:

還有 46% 的精彩內(nèi)容
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
支付 ¥4.99 繼續(xù)閱讀

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容