快速建立一個強大的MongoDB副本集(4種方法)

快速建立一個強大的MongoDB副本集(4種方法)

MongoDB是一個NoSQL資料庫,它使用類似JSON的文件,具有動態模式。當使用資料庫時,有一個應急計劃總是好的,以防你的一個資料庫伺服器發生故障。旁白,你可以通過為你的WordPress網站利用一個靈巧的管理工具來減少發生這種情況的機會。

這就是為什麼有許多資料的副本是有用的。它還可以減少讀取延遲。同時,它可以提高資料庫的可擴充套件性和可用性。這就是副本的作用。它被定義為在多個資料庫之間同步資料的做法。

在這篇文章中,我們將深入探討MongoDB複製副本集的各個突出方面,比如它的功能和機制,僅舉幾例。

  1. 什麼是MongoDB的副本?
  2. 什麼是MongoDB副本集?
  3. 副本在MongoDB中是如何工作的?
  4. 如何建立一個MongoDB副本集?
  5. 處理副本延遲的問題
  6. MongoDB副本集的故障排除
  7. 使用金鑰檔案認證確保安全通訊

什麼是MongoDB的副本?

在MongoDB中,副本集執行復制。這是一組伺服器通過複製維護相同的資料集。你甚至可以將MongoDB副本作為負載平衡的一部分。在這裡,你可以根據使用情況,在所有的例項上分配寫和讀的操作。

什麼是MongoDB副本集?

屬於某個副本集的每個MongoDB例項都是一個成員。每個副本集都需要有一個主要成員和至少一個輔助成員。

主要成員是與副本集進行交易的主要訪問點。它也是唯一可以接受寫操作的成員。副本首先複製主成員的OPLOG(操作日誌)。接下來,它在第二層的各自資料集上重複記錄的變化。因此,每個副本集在同一時間只能有一個主要成員。不同的主站接受寫操作會導致資料衝突。

通常情況下,應用程式只查詢主要成員的寫和讀操作。你可以設計你的設定,從一個或多個輔助成員中讀取。非同步資料傳輸會導致二級節點的讀取為舊資料服務。因此,這樣的安排並不是每個用例的理想選擇。

副本集的特點

自動故障轉移機制使MongoDB的副本集在其競爭中脫穎而出。在沒有主節點的情況下,副節點之間的自動選舉會選出一個新的主節點。

MongoDB副本集vs MongoDB叢集

一個MongoDB副本集將在副本集節點上建立同一資料集的各種副本。副本集的主要目的是:

  • 提供一個內建的備份解決方案
  • 增加資料的可用性

MongoDB叢集是一個完全不同的球賽。它通過一個分片鍵將資料分佈在許多節點上。這個過程將把資料分割成許多碎片,稱為分片。接下來,它將每個分片副本到不同的節點上。叢集的目的是支援大型資料集和高吞吐量操作。它通過橫向擴充套件工作負載來實現這一目標。

下面是副本集和叢集的區別,通俗地說:

  • 叢集分配了工作負載。它還在許多伺服器上儲存資料片段(碎片)。
  • 副本集則完全複製了資料集。

MongoDB允許你通過建立一個分片叢集來結合這些功能。在這裡,你可以將每個分片複製到一個輔助伺服器。這使得分片可以提供高冗餘度和資料可用性。

維護和設定一個副本集在技術上是很費力和費時的。而找到合適的託管服務?這又是一個令人頭痛的問題。有這麼多的選擇,很容易在研究上浪費時間,而不是建立你的業務。

讓我給你介紹一個工具,它可以做所有這些事情,還有更多,這樣你就可以回去用你的服務/產品粉碎它。

MongoDB中的副本是如何工作的?

在MongoDB中,你將寫操作傳送到主伺服器(節點)。主伺服器將這些操作分配到次伺服器上,複製資料。

MongoDB複製過程圖解

MongoDB複製過程圖解(圖片來源:MongoDB

在三種型別的MongoDB節點中,有兩種曾經出現過:主節點和次節點。第三種在複製過程中派上用場的MongoDB節點是一個仲裁者。仲裁者節點沒有資料集的副本,不能成為主節點。雖然如此,仲裁者確實參與了主節點的選舉。

我們之前提到過當主節點癱瘓時會發生什麼,但如果次要節點被咬了怎麼辦?在這種情況下,主節點變成了次要節點,資料庫變得無法訪問。

成員選舉

選舉可以在以下情況下發生:

  • 初始化一個副本集
  • 失去與主節點的連線(可以通過心跳檢測)。
  • 使用 rs.reconfig 或 stepDown 方法維護一個副本集
  • 向現有的副本集新增一個新的節點

一個副本集最多可以擁有50個成員,但只有7個或更少的成員可以在任何選舉中投票。

叢集選舉一個新的主節點之前的平均時間不應該超過12秒。選舉演算法將試圖讓具有最高優先順序的副手可用。同時,優先順序值為0的成員不能成為主選,不參與選舉。

二級節點成為一級節點

二級節點成為一級節點(圖片來源:Medium

寫的問題

對於耐用性,寫操作有一個框架,在指定的節點數量中複製資料。你甚至可以藉此向客戶端提供反饋。這個框架也被稱為 “寫關注”。它有資料承載成員,在操作返回成功之前,需要確認寫關注。一般來說,副本集的值是1,作為一個寫關注。因此,在返回寫關注的確認之前,只有主站應該確認寫。

你甚至可以增加確認寫操作所需的成員數量。你可以擁有的成員數量沒有上限。但是,如果數量很高,你需要處理高延遲的問題。這是因為客戶端需要等待所有成員的確認。另外,你可以設定 “多數人 “的寫入關注。這是在收到一半以上的成員的確認後計算出來的。

讀取偏好

對於讀取操作,你可以提到讀取偏好,它描述了資料庫如何將查詢指向副本集的成員。一般來說,主節點會收到讀取操作,但客戶可以提及讀取偏好,將讀取操作傳送到二級節點。以下是讀取偏好的選項:

  • primaryPreferred:通常情況下,讀取操作來自主節點,但如果這一點不可用,資料就會從二級節點中提取。
  • primary:所有的讀取操作都來自於主節點。
  • secondary:所有的讀取操作都由二級節點執行。
  • nearest:這裡,讀取請求被路由到最近的可達節點,這可以通過執行 ping 命令來檢測。讀取操作的結果可以來自副本集的任何成員,不管它是主節點還是次節點。
  • secondaryPreferred:在這裡,大部分的讀取操作來自於二級節點,但如果其中有無可用的節點,則從主節點獲取資料。

副本集的資料同步

為了維護共享資料集的最新副本,副本集的二級成員從其他成員那裡複製或同步資料。

MongoDB利用了兩種形式的資料同步。初始同步,用完整的資料集來填充新成員。複製以執行對完整資料集的持續更改。

初始同步

在初始同步期間,輔助節點執行 init sync 命令,將所有資料從主節點同步到另一個包含最新資料的輔助節點。因此,二級節點始終利用 tailable cursor 功能來查詢主節點的local.oplog.rs集合內的最新oplog條目,並在這些oplog條目內應用這些操作。

從MongoDB 5.2開始,初始同步可以是基於檔案拷貝或邏輯同步。

(1)邏輯同步

當你執行邏輯同步時,MongoDB:

  1. 在為每個集合副本檔案時,開發所有的集合索引。
  2. 副本除本地資料庫以外的所有資料庫。mongod 掃描所有源資料庫中的每個集合,並將所有資料插入其副本的這些集合中。
  3. 執行資料集上的所有變化。通過利用源資料庫的OPLOG,mongod 升級其資料集以描述副本集的當前狀態。
  4. 在資料複製過程中提取新新增的OPLOG記錄。確保目標成員在本地資料庫內有足夠的磁碟空間,以便在這個資料複製階段的時間內暫存這些otlog記錄。

當初始同步完成後,成員從 STARTUP2 過渡到 SECONDARY

(2)基於檔案拷貝的初始同步

一開始,只有當你使用MongoDB企業版時才能執行這個。這個過程通過複製和移動檔案系統上的檔案來執行初始同步。這種同步方法在某些情況下可能比邏輯初始同步更快。請記住,如果你在沒有查詢謂詞的情況下執行count()方法,基於檔案副本的初始同步可能導致不準確的計數。

但是,這種方法也有它的侷限性。

  • 在基於檔案拷貝的初始同步過程中,你不能寫到被同步的成員的本地資料庫中。你也不能在被同步的成員或被同步的成員上執行備份。
  • 當利用加密儲存引擎時,MongoDB使用源金鑰對目標進行加密。
  • 你每次只能從一個給定的成員執行初始同步。

副本

二級成員在初始同步後持續複製資料。二級成員將副本他們從源頭同步的OPLOG,並在一個非同步過程中執行這些操作。

二級成員能夠根據其他成員副本的ping時間和狀態的變化,根據需要自動修改其從源同步。

(1)流式副本

從MongoDB 4.4開始,來自源的同步向其同步的第二方傳送連續的OPLOG條目流。流式副本減少了高負荷和高延遲網路中的副本滯後。它還可以:

  • 減少由於主站故障切換而導致的 w:1 的寫操作丟失的風險。
  • 減少從第二級讀取的滯後性。
  • 減少 w:“majority” 和 w:>1的寫操作的延遲。簡而言之,任何需要等待副本的寫關注。

(2)多執行緒副本

MongoDB習慣於通過多執行緒分批寫操作,以提高併發性。MongoDB通過文件ID對批次進行分組,同時用不同的執行緒應用每組操作。

MongoDB總是以其原始的寫入順序執行對給定文件的寫入操作。這在MongoDB 4.0中有所改變。

從MongoDB 4.0開始,如果讀取發生在應用副本批次的二級系統上,針對二級系統並配置了 “majority” 或 “local” 的讀取操作現在將從WiredTiger的資料快照讀取。從快照中讀取保證了資料的一致性,並讓讀取與正在進行的副本同時發生而不需要鎖。

因此,需要這些讀取關注級別的次級讀取不再需要等待副本批次的應用,可以在收到時進行處理。

如何建立一個MongoDB的副本集

如前所述,MongoDB通過副本集處理副本。在接下來的幾節中,我們將強調一些方法,你可以用這些方法來為你的使用情況建立副本集。

方法一:在Ubuntu上建立一個新的MongoDB副本集

在我們開始之前,你需要確保你至少有三臺執行Ubuntu 20.04的伺服器,每臺伺服器上都安裝有MongoDB

要建立一個副本集,必須提供一個地址,讓副本集的每個成員都能被副本集中的其他人聯絡到。在這種情況下,我們在該組中保留三個成員。雖然我們可以使用IP地址,但不建議這樣做,因為地址可能會意外地改變。一個更好的選擇是在配置副本集時使用邏輯的DNS主機名。

我們可以通過為每個副本成員配置子域來做到這一點。雖然這可能是生產環境的理想選擇,但本節將概述如何通過編輯每個伺服器各自的hosts檔案來配置DNS解析。這個檔案允許我們將可讀的主機名分配給數字IP地址。因此,如果在任何情況下,你的IP地址發生了變化,你所要做的就是更新三個伺服器上的hosts’檔案,而不是從頭開始重新配置副本集!

大多數情況下,hosts 被儲存在 /etc/ 目錄下。在你的三臺伺服器上分別重複下面的命令:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
sudo nano /etc/hosts
sudo nano /etc/hosts
sudo nano /etc/hosts

在上面的命令中,我們使用nano作為我們的文字編輯器,然而,你可以使用你喜歡的任何文字編輯器。在配置本地主機的前幾行之後,為副本集的每個成員新增一個條目。這些條目的形式是一個IP地址,後面是你選擇的可讀名稱。雖然你可以隨心所欲地命名它們,但一定要有描述性,這樣你才知道如何區分每個成員。在本教學中,我們將使用下面的主機名:

  • mongo0.replset.member
  • mongo1.replset.member
  • mongo2.replset.member

使用這些主機名,你的/etc/hosts檔案將看起來類似於以下突出顯示的行:

主機名圖解

主機名圖解

儲存並關閉該檔案。

在為副本集配置了DNS解析後,我們需要更新防火牆規則,以允許它們相互通訊。在mongo0上執行以下 ufw 命令,為mongo1提供對mongo0上27017埠的訪問:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
sudo ufw allow from mongo1_server_ip to any port 27017
sudo ufw allow from mongo1_server_ip to any port 27017
sudo ufw allow from mongo1_server_ip to any port 27017

在 mongo1_server_ip 引數的位置,輸入mongo1伺服器的實際IP地址。另外,如果你已經更新了該伺服器上的Mongo例項以使用非預設的埠,請確保改變27017以反映你的MongoDB例項正在使用的埠。

現在新增另一個防火牆規則,讓mongo2訪問同一個埠:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
sudo ufw allow from mongo2_server_ip to any port 27017
sudo ufw allow from mongo2_server_ip to any port 27017
sudo ufw allow from mongo2_server_ip to any port 27017

在 mongo2_server_ip 引數的地方,輸入你的mongo2伺服器的實際IP地址。然後,為你的另外兩個伺服器更新防火牆規則。在mongo1伺服器上執行以下命令,確保改變server_ip引數中的IP地址,以分別反映mongo0和mongo2的地址:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
sudo ufw allow from mongo0_server_ip to any port 27017
sudo ufw allow from mongo0_server_ip to any port 27017
sudo ufw allow from mongo0_server_ip to any port 27017
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
sudo ufw allow from mongo2_server_ip to any port 27017
sudo ufw allow from mongo2_server_ip to any port 27017
sudo ufw allow from mongo2_server_ip to any port 27017

最後,在mongo2上執行這兩條命令。同樣,要確保你為每臺伺服器輸入正確的IP地址:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
sudo ufw allow from mongo0_server_ip to any port 27017
sudo ufw allow from mongo0_server_ip to any port 27017
sudo ufw allow from mongo0_server_ip to any port 27017
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
sudo ufw allow from mongo1_server_ip to any port 27017
sudo ufw allow from mongo1_server_ip to any port 27017
sudo ufw allow from mongo1_server_ip to any port 27017

你的下一步是更新每個MongoDB例項的配置檔案以允許外部連線。為了允許這一點,你需要修改每臺伺服器的配置檔案,以反映IP地址並指明副本集。雖然你可以使用任何首選的文字編輯器,但我們再次使用nano文字編輯器。讓我們在每個mongod.conf檔案中做如下修改。

On mongo0:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# network interfaces
net:
port: 27017
bindIp: 127.0.0.1,mongo0.replset.member# replica set
replication:
replSetName: "rs0"
# network interfaces net: port: 27017 bindIp: 127.0.0.1,mongo0.replset.member# replica set replication: replSetName: "rs0"
# network interfaces
net:
port: 27017
bindIp: 127.0.0.1,mongo0.replset.member# replica set
replication:
replSetName: "rs0"

On mongo1:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# network interfaces
net:
port: 27017
bindIp: 127.0.0.1,mongo1.replset.member
replication:
replSetName: "rs0"
# network interfaces net: port: 27017 bindIp: 127.0.0.1,mongo1.replset.member replication: replSetName: "rs0"
# network interfaces
net:
port: 27017
bindIp: 127.0.0.1,mongo1.replset.member
replication:
replSetName: "rs0"

On mongo2:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# network interfaces
net:
port: 27017
bindIp: 127.0.0.1,mongo2.replset.member
replication:
replSetName: "rs0"
# network interfaces net: port: 27017 bindIp: 127.0.0.1,mongo2.replset.member replication: replSetName: "rs0"
# network interfaces
net:
port: 27017
bindIp: 127.0.0.1,mongo2.replset.member
replication:
replSetName: "rs0"

Important

如果你想改變rs0的指令值,請確保在上述命令中替換相同的指令。你可以把指令命名為任何你想要的東西,但是,請確保它有足夠的描述性。更新完成後,在每個例項中重新啟動mongod服務以重新載入配置。

sudo systemctl restart mongod

這樣,你就為每個伺服器的MongoDB例項啟用了副本功能。

現在你可以通過使用 rs.initiate() 方法來初始化副本集。這個方法只需要在副本集中的單個MongoDB例項上執行。確保副本集的名稱和成員與你之前在每個配置檔案中的配置一致。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
rs.initiate(
{
_id: "rs0",
members: [
{ _id: 0, host: "mongo0.replset.member" },
{ _id: 1, host: "mongo1.replset.member" },
{ _id: 2, host: "mongo2.replset.member" }
]
})
rs.initiate( { _id: "rs0", members: [ { _id: 0, host: "mongo0.replset.member" }, { _id: 1, host: "mongo1.replset.member" }, { _id: 2, host: "mongo2.replset.member" } ] })
rs.initiate(
{
_id: "rs0",
members: [
{ _id: 0, host: "mongo0.replset.member" },
{ _id: 1, host: "mongo1.replset.member" },
{ _id: 2, host: "mongo2.replset.member" }
]
})

如果該方法返回 “ok”。1,這意味著副本集被正確啟動。下面是一個輸出結果的例子,應該是這樣的:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
{
"ok": 1,
"$clusterTime": {
"clusterTime": Timestamp(1612389071, 1),
"signature": {
"hash": BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId": NumberLong(0)
}
},
"operationTime": Timestamp(1612389071, 1)
}
{ "ok": 1, "$clusterTime": { "clusterTime": Timestamp(1612389071, 1), "signature": { "hash": BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId": NumberLong(0) } }, "operationTime": Timestamp(1612389071, 1) }
{
"ok": 1,
"$clusterTime": {
"clusterTime": Timestamp(1612389071, 1),
"signature": {
"hash": BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId": NumberLong(0)
}
},
"operationTime": Timestamp(1612389071, 1)
}

關閉MongoDB伺服器

你可以通過使用 db.shutdownServer() 方法關閉MongoDB伺服器。下面是同樣的語法。force 和 timeoutsecs 都是可選引數

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
db.shutdownServer({
force: <boolean>,
timeoutSecs: <int>
})
db.shutdownServer({ force: <boolean>, timeoutSecs: <int> })
db.shutdownServer({
force: <boolean>,
timeoutSecs: <int>
})

如果mongod replica set成員在建立索引時執行某些操作,這個方法可能會失敗。要中斷這些操作並強制成員關閉,你可以將布林引數 force 輸入為true。

用-replSet重啟MongoDB

要重置配置,請確保副本組中的每個節點都已停止。然後刪除每個節點的本地資料庫。使用 –replSet 標誌再次啟動它,並且只在副本集的一個mongod例項上執行 rs.initiate() 。

mongod --replSet "rs0"

rs.initiate() 可以接受一個可選的副本集配置檔案,即:

  • Replication.replSetName 或 —replSet 選項,在 _id 欄位中指定副本集名稱。
  • 成員陣列,其中包含每個副本整合員的一個檔案。

rs.initiate() 方法觸發一個選舉,並選出其中一個成員作為主選。

將成員新增到副本集

為了向該集合新增成員,在不同的機器上啟動mongod例項。接下來,啟動一個mongo客戶端並使用 rs.add() 命令。

rs.add() 命令的基本語法如下:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
rs.add(HOST_NAME:PORT)
rs.add(HOST_NAME:PORT)
rs.add(HOST_NAME:PORT)

比如說,

假設mongo1是你的mongod例項,它在27017埠監聽。使用Mongo客戶端命令 rs.add() 將這個例項新增到副本集中。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
rs.add("mongo1:27017")
rs.add("mongo1:27017")
rs.add("mongo1:27017")

只有在你連線到主節點後,你才能將mongod例項新增到副本集。要驗證你是否連線到主節點,可以使用 db.isMaster() 命令。

移除使用者

要移除一個成員,我們可以使用 rs.remove()

要做到這一點,首先,通過使用我們上面討論的 db.shutdownServer() 方法,關閉你想刪除的mongod例項。

接下來,連線到副本集的當前主節點。要確定當前的主副本,可以在連線到副本集的任何成員時使用 db.hello()。一旦你確定了主站,執行下面的命令:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
rs.remove("mongodb-node-04:27017")
rs.remove("mongodb-node-04")
rs.remove("mongodb-node-04:27017") rs.remove("mongodb-node-04")
rs.remove("mongodb-node-04:27017")
rs.remove("mongodb-node-04")

節點被成功地從副本集中移除

上面的圖片顯示,該節點被成功地從副本集中移除。(圖片來源:Bmc)

如果副本集需要選出一個新的主節點,MongoDB可能會短暫地斷開shell的連線。在這種情況下,它將自動再次重新連線。另外,它可能會顯示一個 DBClientCursor::init call() 失敗的錯誤,即使命令成功了。

方法二:配置MongoDB副本集進行部署和測試

一般來說,你可以在啟用或禁用RBAC的情況下為測試設定副本集。在這個方法中,我們將在禁用訪問控制的情況下設定副本集,以便在測試環境中部署。

首先,使用下面的命令為所有屬於副本集的例項建立目錄:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mkdir -p /srv/mongodb/replicaset0-0 /srv/mongodb/replicaset0-1 /srv/mongodb/replicaset0-2
mkdir -p /srv/mongodb/replicaset0-0 /srv/mongodb/replicaset0-1 /srv/mongodb/replicaset0-2
mkdir -p /srv/mongodb/replicaset0-0  /srv/mongodb/replicaset0-1 /srv/mongodb/replicaset0-2

這個命令將為三個MongoDB例項replicaset0-0、replicaset0-1和replicaset0-2建立目錄。現在,使用下面這組命令為每個例項啟動MongoDB例項:

For Server 1:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mongod --replSet replicaset --port 27017 --bind_ip localhost,<hostname(s)|ip address(es)> --dbpath /srv/mongodb/replicaset0-0 --oplogSize 128
mongod --replSet replicaset --port 27017 --bind_ip localhost,<hostname(s)|ip address(es)> --dbpath /srv/mongodb/replicaset0-0 --oplogSize 128
mongod --replSet replicaset --port 27017 --bind_ip localhost,<hostname(s)|ip address(es)> --dbpath /srv/mongodb/replicaset0-0  --oplogSize 128

For Server 2:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mongod --replSet replicaset --port 27018 --bind_ip localhost,<hostname(s)|ip address(es)> --dbpath /srv/mongodb/replicaset0-0 --oplogSize 128
mongod --replSet replicaset --port 27018 --bind_ip localhost,<hostname(s)|ip address(es)> --dbpath /srv/mongodb/replicaset0-0 --oplogSize 128
mongod --replSet replicaset --port 27018 --bind_ip localhost,<hostname(s)|ip address(es)> --dbpath /srv/mongodb/replicaset0-0  --oplogSize 128

For Server 3:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mongod --replSet replicaset --port 27019 --bind_ip localhost,<hostname(s)|ip address(es)> --dbpath /srv/mongodb/replicaset0-0 --oplogSize 128
mongod --replSet replicaset --port 27019 --bind_ip localhost,<hostname(s)|ip address(es)> --dbpath /srv/mongodb/replicaset0-0 --oplogSize 128
mongod --replSet replicaset --port 27019 --bind_ip localhost,<hostname(s)|ip address(es)> --dbpath /srv/mongodb/replicaset0-0  --oplogSize 128

–oplogSize 引數用於防止機器在測試階段過載。它有助於減少每個磁碟所消耗的磁碟空間。

現在,通過使用下面的埠號,用Mongo shell連線到其中一個例項。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mongo --port 27017
mongo --port 27017
mongo --port 27017

我們可以使用 rs.initiate() 命令來啟動副本過程。你必須用你的系統名稱替換 hostname 引數。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
rs conf = {
_id: "replicaset0",
members: [
{ _id: 0, host: "<hostname>:27017},
{ _id: 1, host: "<hostname>:27018"},
{ _id: 2, host: "<hostname>:27019"}
] }
rs conf = { _id: "replicaset0", members: [ { _id: 0, host: "<hostname>:27017}, { _id: 1, host: "<hostname>:27018"}, { _id: 2, host: "<hostname>:27019"} ] }
rs conf = {
_id: "replicaset0",
members: [
{  _id: 0,  host: "<hostname>:27017},
{  _id: 1,  host: "<hostname>:27018"},
{  _id: 2,  host: "<hostname>:27019"}
] }

現在你可以把配置物件檔案作為initiate命令的引數傳遞給它,並按如下方式使用:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
rs.initiate(rsconf)
rs.initiate(rsconf)
rs.initiate(rsconf)

就這樣,你擁有了它! 你已經成功建立了一個用於開發和測試的MongoDB副本集。

方法三:將獨立例項轉化為MongoDB副本集

MongoDB允許其使用者將他們的獨立例項轉化為副本集。獨立例項主要用於測試和開發階段,而副本集是生產環境的一部分。

為了開始,讓我們使用以下命令關閉我們的mongod例項:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
db.adminCommand({"shutdown":"1"})
db.adminCommand({"shutdown":"1"})
db.adminCommand({"shutdown":"1"})

通過在命令中使用 –repelSet 引數來指定你要使用的副本集,重新啟動你的例項:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mongod --port 27017 – dbpath /var/lib/mongodb --replSet replicaSet1 --bind_ip localhost,<hostname(s)|ip address(es)>
mongod --port 27017 – dbpath /var/lib/mongodb --replSet replicaSet1 --bind_ip localhost,<hostname(s)|ip address(es)>
mongod --port 27017 – dbpath /var/lib/mongodb  --replSet replicaSet1 --bind_ip localhost,<hostname(s)|ip address(es)>

你必須在命令中指定你的伺服器的名稱以及唯一的地址。

將shell與你的MongoDB例項連線,並使用initiate命令啟動副本過程,併成功地將例項轉換為副本集。你可以使用以下命令執行所有的基本操作,比如新增或刪除一個例項:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
rs.add(“<host_name:port>”)
rs.add(“<host_name:port>”)
rs.add(“<host_name:port>”)
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
rs.remove(“host-name”)
rs.remove(“host-name”)
rs.remove(“host-name”)

此外,你可以使用 rs.status() 和 rs.conf() 命令檢查你的MongoDB副本集的狀態。

方法四:MongoDB Atlas – 一個更簡單的替代方案

副本和分片可以一起工作,形成一個叫做分片叢集的東西。雖然設定和配置可能相當耗時,儘管很簡單,但MongoDB Atlas是一個比前面提到的方法更好的選擇。

它可以自動化你的副本集,使這個過程容易實現。它可以通過幾次點選來部署全域性分片副本集,實現災難恢復,更容易管理,資料定位,以及多區域部署。

在MongoDB Atlas中,我們需要建立叢集–它們可以是一個副本集,也可以是一個分片叢集。對於一個特定的專案,其他地區的叢集中的節點數量被限制在總共40個。

這不包括免費或共享的叢集和相互通訊的谷歌雲區域。任何兩個區域之間的節點總數必須滿足這個約束。例如,如果有一個專案在其中:

  • 區域A有15個節點。
  • 區域B有25個節點
  • 區域C有10個節點

我們只能再分配5個節點給區域C,因為:

  1. 區域A+區域B=40;滿足40是允許的最大節點數的約束。
  2. 區域B+區域C=25+10+5(分配給C的額外節點)=40;滿足40是允許的最大節點數的約束。
  3. 區域A+區域C=15+10+5(分配給C的額外節點)=30;滿足40是允許的最大節點數的約束。

如果我們再分配10個節點給區域C,使區域C有20個節點,那麼區域B+區域C=45個節點。這將超過給定的約束條件,所以你可能無法建立一個多區域叢集。

當你建立一個叢集時,Atlas會在專案中為雲提供商建立一個網路容器,如果它之前不在那裡。要在MongoDB Atlas中建立一個副本集叢集,請在Atlas CLI中執行以下命令:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
atlas clusters create [name] [options]
atlas clusters create [name] [options]
atlas clusters create [name] [options]

請確保你給出一個描述性的叢集名稱,因為在叢集建立後它不能被改變。該引數可以包含ASCII字母、數字和連字元。

在MongoDB中,根據你的要求,有幾個選項可用於建立叢集。例如,如果你想為你的叢集進行連續的雲備份,請將 --backup 設定為 true。

處理副本延遲的問題

副本延遲可能相當令人腦瓜疼的事情。它是指在主伺服器上的操作和從OPLOG到輔助伺服器上的應用之間的延遲。如果你的業務是處理大型資料集,在一定的閾值內,延遲是可以預期的。然而,有時外部因素也可能造成並增加延遲。為了從最新的副本中獲益,請確保:

  1. 你在一個穩定和足夠的頻寬中路由你的網路流量。網路延遲在影響你的副本方面起著巨大的作用,如果網路不足以滿足複製過程的需要,那麼在整個副本集中複製資料就會出現延遲。
  2. 你有足夠的磁碟吞吐量。如果輔助節點上的檔案系統和磁碟裝置不能像主節點那樣快速地將資料刷入磁碟,那麼輔助節點將難以跟上。因此,次級節點處理寫查詢的速度比主節點慢。這是大多數多使用者系統中的一個常見問題,包括虛擬化例項和大規模部署。
  3. 你在一個時間間隔後請求寫確認寫關注,以提供機會讓次要節點趕上主節點,特別是當你想執行一個批量載入操作或資料攝取,需要大量寫到主節點時。二級系統將無法快速讀取OPLOG以跟上變化;特別是在未確認的寫關注方面。
  4. 你確定正在執行的後臺任務。某些任務如cron job、伺服器更新和安全檢查可能會對網路或磁碟的使用產生意想不到的影響,導致複製過程的延遲。

如果你不確定你的應用程式中是否存在副本滯後,不要著急–下一節討論了故障排除策略!

診斷MongoDB副本集的故障

你已經成功地設定了你的副本集,但你注意到你的資料在伺服器之間不一致。這對大型企業來說是非常令人震驚的,然而,通過快速的故障排除方法,你可能會找到原因,甚至糾正這個問題! 下面是一些常見的副本集部署的故障排除策略,可能會派上用場:

檢查副本狀態

我們可以通過在連線到副本集主站的mongosh會話中執行以下命令來檢查副本集的當前狀態和每個成員的狀態。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
rs.status()
rs.status()
 rs.status()

檢查副本滯

正如前面所討論的,副本滯後可能是一個嚴重的問題,因為它使 “lagged” 的成員沒有資格迅速成為主要成員,並增加了分散式讀操作不一致的可能性。我們可以用下面的命令來檢查副本日誌的當前長度:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
rs.printSecondaryReplicationInfo()
rs.printSecondaryReplicationInfo()
rs.printSecondaryReplicationInfo()

這將返回 syncedTo 值,該值是每個成員的最後一條OPLOG條目被寫入二級資料庫的時間。這裡有一個例子來證明這一點:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
source: m1.example.net:27017
syncedTo: Mon Oct 10 2022 10:19:35 GMT-0400 (EDT)
0 secs (0 hrs) behind the primary
source: m2.example.net:27017
syncedTo: Mon Oct 10 2022 10:19:35 GMT-0400 (EDT)
0 secs (0 hrs) behind the primary
source: m1.example.net:27017 syncedTo: Mon Oct 10 2022 10:19:35 GMT-0400 (EDT) 0 secs (0 hrs) behind the primary source: m2.example.net:27017 syncedTo: Mon Oct 10 2022 10:19:35 GMT-0400 (EDT) 0 secs (0 hrs) behind the primary
source: m1.example.net:27017
syncedTo: Mon Oct 10 2022 10:19:35 GMT-0400 (EDT)
0 secs (0 hrs) behind the primary
source: m2.example.net:27017
syncedTo: Mon Oct 10 2022 10:19:35 GMT-0400 (EDT)
0 secs (0 hrs) behind the primary

當主站的不活動期大於 members[n].secondaryDelaySecs 值時,一個延遲的成員可能顯示為比主站晚0秒。

測試所有成員之間的連線

副本集的每個成員必須能夠與其他每個成員連線。一定要確保驗證兩個方向的連線。大多數情況下,防火牆配置或網路拓撲結構會阻止正常和必要的連線,這可能會阻礙副本。

例如,讓我們假設mongod例項同時繫結到localhost和與IP地址198.41.110.1相關的主機名’ExampleHostname’:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mongod --bind_ip localhost, ExampleHostname
mongod --bind_ip localhost, ExampleHostname
mongod --bind_ip localhost, ExampleHostname

要連線到這個例項,遠端客戶必須指定主機名或IP地址。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mongosh --host ExampleHostname
mongosh --host 198.41.110.1
mongosh --host ExampleHostname mongosh --host 198.41.110.1
mongosh --host ExampleHostname
mongosh --host 198.41.110.1

如果一個副本集由三個成員組成,即m1、m2和m3,使用預設埠27017,你應該按以下方式測試連線:

On m1:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mongosh --host m2 --port 27017
mongosh --host m3 --port 27017
mongosh --host m2 --port 27017 mongosh --host m3 --port 27017
mongosh --host m2 --port 27017
mongosh --host m3 --port 27017

On m2:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mongosh --host m1 --port 27017
mongosh --host m3 --port 27017
mongosh --host m1 --port 27017 mongosh --host m3 --port 27017
mongosh --host m1 --port 27017
mongosh --host m3 --port 27017

On m3:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mongosh --host m1 --port 27017
mongosh --host m2 --port 27017
mongosh --host m1 --port 27017 mongosh --host m2 --port 27017
mongosh --host m1 --port 27017
mongosh --host m2 --port 27017

如果任何方向的連線都失敗了,你必須檢查你的防火牆配置,並重新配置以允許連線。

用金鑰檔案認證確保安全通訊

預設情況下,MongoDB中的金鑰檔案認證依賴於加鹽挑戰響應認證機制(SCRAM)。為了做到這一點,MongoDB必須讀取並驗證使用者提供的憑證,其中包括特定MongoDB例項所知道的使用者名稱、密碼和認證資料庫的組合。這正是用來驗證連線資料庫時提供密碼的使用者的機制。

當你在MongoDB中啟用認證時,基於角色的訪問控制(RBAC)會自動為副本集啟用,使用者被授予一個或多個角色,以決定他們對資料庫資源的訪問。當RBAC被啟用時,意味著只有經過認證的有效Mongo使用者,並具有相應的許可權,才能夠訪問系統中的資源。

金鑰檔案的作用就像叢集中每個成員的共享密碼。這使得副本集中的每個mongod例項能夠使用鑰匙檔案的內容作為共享密碼,以驗證部署中的其他成員。

只有那些擁有正確金鑰檔案的mongod例項才能加入副本集。一個金鑰的長度必須在6到1024個字元之間,並且只能包含base64集的字元。請注意,MongoDB在讀取金鑰時,會刪除空白字元。

你可以使用各種方法來生成一個金鑰檔案。在本教學中,我們使用 openssl 生成一個複雜的1024個隨機字元的字串,作為共享密碼使用。然後,它使用 chmod 來改變檔案許可權,只為檔案所有者提供閱讀許可權。避免將金鑰檔案儲存在容易與託管mongod例項的硬體斷開連線的儲存介質上,如USB驅動器或網路連線的儲存裝置。下面是生成金鑰檔案的命令:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
openssl rand -base64 756 > <path-to-keyfile>
chmod 400 <path-to-keyfile>
openssl rand -base64 756 > <path-to-keyfile> chmod 400 <path-to-keyfile>
openssl rand -base64 756 > <path-to-keyfile>
chmod 400 <path-to-keyfile>

接下來,將金鑰檔案複製到每個副本整合員。確保執行mongod例項的使用者是該檔案的所有者,可以訪問該金鑰檔案。在你完成上述工作後,從第二層開始,關閉副本組的所有成員。一旦所有的輔助裝置都離線,你就可以繼續關閉主裝置。必須遵循這個順序,以防止潛在的回滾。現在通過執行以下命令關閉mongod例項:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
use admin
db.shutdownServer()
use admin db.shutdownServer()
use admin
db.shutdownServer()

命令執行後,副本集的所有成員都將離線。現在,重新啟動副本集的每個成員,並啟用訪問控制

對於副本集的每個成員,用 security.keyFile 配置檔案設定或 --keyFile 命令列選項啟動mongod例項。

如果你使用的是一個配置檔案,請將

  • security.keyFile為keyfile的路徑,而
  • replication.replSetName為副本集名稱。
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
security:
keyFile: <path-to-keyfile>
replication:
replSetName: <replicaSetName>
net:
bindIp: localhost,<hostname(s)|ip address(es)>
security: keyFile: <path-to-keyfile> replication: replSetName: <replicaSetName> net: bindIp: localhost,<hostname(s)|ip address(es)>
security:
keyFile: <path-to-keyfile>
replication:
replSetName: <replicaSetName>
net:
bindIp: localhost,<hostname(s)|ip address(es)>

使用配置檔案啟動mongod例項:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mongod --config <path-to-config-file>
mongod --config <path-to-config-file>
mongod --config <path-to-config-file>

如果你使用的是命令列選項,請用以下選項啟動mongod例項:

  • -keyFile設定為keyfile的路徑,和
  • -replSet設定為副本集的名稱。
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mongod --keyFile <path-to-keyfile> --replSet <replicaSetName> --bind_ip localhost,<hostname(s)|ip address(es)>
mongod --keyFile <path-to-keyfile> --replSet <replicaSetName> --bind_ip localhost,<hostname(s)|ip address(es)>
mongod --keyFile <path-to-keyfile> --replSet <replicaSetName> --bind_ip localhost,<hostname(s)|ip address(es)>

你可以根據你的配置需要包括額外的選項。例如,如果你希望遠端客戶端連線到你的部署或你的部署成員在不同的主機上執行,指定-bind_ip。欲瞭解更多資訊,請參見本地主機繫結相容性變化

接下來,通過 localhost 介面連線到副本集的一個成員。你必須在與mongod例項相同的物理機上執行mongosh。這個介面只有在沒有為部署建立使用者時才可用,並且在建立第一個使用者後自動關閉。

然後我們啟動副本集。在mongosh中,執行 rs.initiate() 方法:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
rs.initiate(
{
_id: "myReplSet",
members: [
{ _id: 0, host: "mongo1:27017" },
{ _id: 1, host: "mongo2:27017" },
{ _id: 2, host: "mongo3:27017" }
]
}
)
rs.initiate( { _id: "myReplSet", members: [ { _id: 0, host: "mongo1:27017" }, { _id: 1, host: "mongo2:27017" }, { _id: 2, host: "mongo3:27017" } ] } )
rs.initiate(
{
_id: "myReplSet",
members: [
{ _id: 0, host: "mongo1:27017" },
{ _id: 1, host: "mongo2:27017" },
{ _id: 2, host: "mongo3:27017" }
]
}
)

正如之前所討論的,這個方法會選擇其中一個成員作為副本集的主要成員。要找到主要成員,請使用rs.status()。在繼續之前連線到主成員。

現在,建立使用者管理員。你可以使用 db.createUser() 方法新增一個使用者。確保該使用者在管理資料庫上至少要有 userAdminAnyDatabase 角色。

下面的例子建立了使用者 “batman”,他在管理資料庫上具有 userAdminAnyDatabase 的角色:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
admin = db.getSiblingDB("admin")
admin.createUser(
{
user: "batman",
pwd: passwordPrompt(), // or cleartext password
roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
}
)
admin = db.getSiblingDB("admin") admin.createUser( { user: "batman", pwd: passwordPrompt(), // or cleartext password roles: [ { role: "userAdminAnyDatabase", db: "admin" } ] } )
admin = db.getSiblingDB("admin")
admin.createUser(
{
user: "batman",
pwd: passwordPrompt(), // or cleartext password
roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
}
)

在提示時輸入之前建立的密碼。

接下來,你必須以管理員的身份進行認證。要做到這一點,使用 db.auth() 來驗證。比如說

db.getSiblingDB(“admin”).auth(“batman”, passwordPrompt()) // 或者明文密碼

另外,你可以使用 -u <username>, -p <password> 和 --authenticationDatabase 引數將一個新的mongosh例項連線到主副本整合員。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mongosh -u "batman" -p --authenticationDatabase "admin"
mongosh -u "batman" -p --authenticationDatabase "admin"
mongosh -u "batman" -p  --authenticationDatabase "admin"

即使你沒有在 -p 命令列欄位中指定密碼,mongosh也會提示你輸入密碼。

最後,建立叢集管理員clusterAdmin 角色授予副本操作的許可權,比如配置副本集。

讓我們建立一個叢集管理員使用者,並在管理資料庫中分配 clusterAdmin 角色:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
db.getSiblingDB("admin").createUser(
{
"user": "robin",
"pwd": passwordPrompt(), // or cleartext password
roles: [ { "role" : "clusterAdmin", "db" : "admin" } ]
}
)
db.getSiblingDB("admin").createUser( { "user": "robin", "pwd": passwordPrompt(), // or cleartext password roles: [ { "role" : "clusterAdmin", "db" : "admin" } ] } )
db.getSiblingDB("admin").createUser(
{
"user": "robin",
"pwd": passwordPrompt(),     // or cleartext password
roles: [ { "role" : "clusterAdmin", "db" : "admin" } ]
}
)

在提示時輸入密碼。

如果你願意,你可以建立額外的使用者,以允許客戶與副本集進行互動。

然後就可以了! 你已經成功地啟用了金鑰檔案認證!

小結

當涉及到資料庫時,副本一直是一個基本要求,特別是當更多的企業擴大規模時。它廣泛提高了系統的效能、資料安全性和可用性。說到效能,對你的WordPress資料庫來說,監測效能問題並在關鍵時刻糾正它們是至關重要的,例如,Jetpack和Freshping就是其中之一。

副本有助於確保跨多個伺服器的資料保護,並防止你的伺服器遭受嚴重的停機(甚至更糟糕的是–完全失去你的資料)。在這篇文章中,我們介紹了副本集的建立和一些故障排除技巧,以及副本的重要性。你是否為你的業務使用MongoDB副本,它是否被證明對你有用?請在下面的評論區告訴我們!

評論留言