2020重新出發,NOSQL,MongoDB分散式叢集架構
MongoDB分散式叢集架構
看到這裡相信你已經掌握了 MongoDB 的大部分基本知識,現在在單機環境下操作 MongoDB 已經不存在問題,但是單機環境只適合學習和開發測試,在實際的生產環境中,MongoDB 基本是以叢集的方式工作的。叢集的工作方式能夠保證在生產遇到故障時及時恢復,保障應用程式正常地執行和資料的安全。
重點介紹 MongoDB 的叢集工作方式,以及在叢集工作方式下,MongoDB 是如何使用分片和複製的機制來完成對資料的管理和恢復的。
MongoDB 有三種叢集部署模式,分別為主從複製(Master-Slaver)、副本集(Replica Set)和分片(Sharding)模式。
- Master-Slaver: 是一種主從副本的模式,目前已經不推薦使用。
- Replica Set :模式取代了 Master-Slaver 模式,是一種互為主從的關係。Replica Set 將資料複製多份儲存,不同伺服器儲存同一份資料,在出現故障時自動切換,實現故障轉移,在實際生產中非常實用。
- Sharding :模式適合處理大量資料,它將資料分開儲存,不同伺服器儲存不同的資料,所有伺服器資料的總和即為整個資料集。
- Sharding 模式追求的是高效能,而且是三種叢集中最複雜的。
在實際生產環境中,通常將 Replica Set 和 Sharding 兩種技術結合使用。
主從複製
雖然 MongoDB 官方建議用副本集替代主從複製,但還是讓大家瞭解 MongoDB 的複製機制。
主從複製是 MongoDB 中最簡單的資料庫同步備份的叢集技術,其基本的設定方式是建立一個主節點(Primary)和一個或多個從節點(Secondary),如圖。
這種方式比單節點的可用性好很多,可用於備份、故障恢復、讀擴充套件等。叢集中的主從節點均執行 MongoDB 例項,完成資料的儲存、查詢與修改操作。
主從複製模式的叢集中只能有一個主節點,主節點提供所有的增、刪、查、改服務,從節點不提供任何服務,但是可以通過設定使從節點提供查詢服務,這樣可以減少主節點的壓力。
另外,每個從節點要知道主節點的地址,主節點記錄在其上的所有操作,從節點定期輪詢主節點獲取這些操作,然後對自己的資料副本執行這些操作,從而保證從節點的資料與主節點一致。
在主從複製的叢集中,當主節點出現故障時,只能人工介入,指定新的主節點,從節點不會自動升級為主節點。同時,在這段時間內,該叢集架構只能處於只讀狀態。
副本集
副本集的叢集架構如下圖所示。
此叢集擁有一個主節點和多個從節點,這一點與主從複製模式類似,且主從節點所負責的工作也類似,但是副本集與主從複製的區別在於:當叢集中主節點發生故障時,副本集可以自動投票,選舉出新的主節點,並引導其餘的從節點連線新的主節點,而且這個過程對應用是透明的。
可以說,MongoDB 的副本集是自帶故障轉移功能的主從複製。
MongoDB 副本集使用的是 N 個 mongod 節點構建的具備自動容錯功能、自動恢復功能的高可用方案。在副本集中,任何節點都可作為主節點,但為了維持資料一致性,只能有一個主節點。
主節點負責資料的寫入和更新,並在更新資料的同時,將操作資訊寫入名為 oplog 的日誌檔案當中。主節點還負責指定其他節點為從節點,並設定從節點資料的可讀性,從而讓從節點來分擔叢集讀取資料的壓力。
另外,從節點會定時輪詢讀取 oplog 日誌,根據日誌內容同步更新自身的資料,保持與主節點一致。
在一些場景中,使用者還可以使用副本集來擴充套件讀效能,客戶端有能力傳送讀寫操作給不同的伺服器,也可以在不同的資料中心獲取不同的副本來擴充套件分散式應用的能力。
在副本集中還有一個額外的仲裁節點(不需要使用專用的硬體裝置),負責在主節點發生故障時,參與選舉新節點作為主節點。
副本集中的各節點會通過心跳資訊來檢測各自的健康狀況,當主節點出現故障時,多個從節點會觸發一次新的選舉操作,並選舉其中一個作為新的主節點。為了保證選舉票數不同,副本集的節點數保持為奇數。
分片
副本集可以解決主節點發生故障導致資料丟失或不可用的問題,但遇到需要儲存海量資料的情況時,副本集機制就束手無策了。副本集中的一臺機器可能不足以儲存資料,或者說叢集不足以提供可接受的讀寫吞吐量。這就需要用到 MongoDB 的分片(Sharding)技術,這也是 MongoDB 的另外一種叢集部署模式。
分片是指將資料拆分並分散存放在不同機器上的過程。有時也用分割槽來表示這個概念。將資料分散到不同的機器上,不需要功能強大的大型計算機就可以儲存更多的資料,處理更大的負載。
MongoDB 支援自動分片,可以使資料庫架構對應用程式不可見,簡化系統管理。對應用程式而言,就如同始終在使用一個單機的 MongoDB 伺服器一樣。
MongoDB 的分片機制允許建立一個包含許多臺機器的叢集,將資料子集分散在叢集中,每個分片維護著一個數據集合的子集。與副本集相比,使用叢集架構可以使應用程式具有更強大的資料處理能力。
MongoDB 分片的叢集模式如下圖所示。
構建一個 MongoDB 的分片叢集,需要三個重要的元件,分別是分片伺服器(Shard Server)、配置伺服器(Config Server)和路由伺服器(Route Server)。
- Shard Server
- 每個 Shard Server 都是一個 mongod 資料庫例項,用於儲存實際的資料塊。整個資料庫集合分成多個塊儲存在不同的 Shard Server 中。
- 在實際生產中,一個 Shard Server 可由幾臺機器組成一個副本集來承擔,防止因主節點單點故障導致整個系統崩潰。
- Config Server
- 這是獨立的一個 mongod 程序,儲存叢集和分片的元資料,在叢集啟動最開始時建立,儲存各個分片包含資料的資訊。
- Route Server
- 這是獨立的一個 mongos 程序,Route Server 在叢集中可作為路由使用,客戶端由此接入,讓整個叢集看起來像是一個單一的資料庫,提供客戶端應用程式和分片叢集之間的介面。
- Route Server 本身不儲存資料,啟動時從 Config Server 載入叢集資訊到快取中,並將客戶端的請求路由給每個 Shard Server,在各 Shard Server 返回結果後進行聚合並返回客戶端。
以上介紹了 MongoDB 的三種叢集模式,副本集已經替代了主從複製,通過備份保證叢集的可靠性,分片機制為叢集提供了可擴充套件性,以滿足海量資料的儲存和分析的需求。
在實際生產環境中,副本集和分片是結合起來使用的,可滿足實際應用場景中高可用性和高可擴充套件性的需求。
將MongoDB部署到分散式叢集(實操)
分散式叢集的整體架構
在實際生產環境中,MongoDB 的叢集架構是分散式的,如下圖所示,叢集會結合副本集和分片機制保證生產過程的高可靠性和高可擴充套件性。
從上圖的叢集中可以看到,整個生產叢集與分片叢集的架構類似,由三個重要元件組成,包括 Shard Server、Config Server 和 Route Server。不同之處在於每個元件可以使用多個例項來保證叢集的可靠性。
- 例如,每一個 Shard Server 由一個包含三個 mongod 例項的副本集組成,避免了單一的 mongod 例項出現故障造成資料的丟失。Config Server 也可由多個 mongod 例項叢集組成,保證叢集中配置資訊的可用性。而路由伺服器也可以使用多個 mongos 例項,來保證客戶端的請求能得到及時的響應。
接下來通過分散式叢集的部署瞭解 MongoDB 的副本集和分片機制。假設目前有三臺機器, 作業系統為 Ubuntu 16.04,均安裝了 MongoDB 3.4,資訊如下表所示。在這三臺機器上部署副本集和部署分片叢集。
主機名 | IP | 埠資訊 |
---|---|---|
Node1 | 10.90.9.101 | momgod shard1 : 27018(rs1) momgod shard2 : 27018(rs2) mongod config1 : 27030 mongos router1 : 27017 |
Node2 | 10.90.9.102 | momgod shard1 : 27018(rs1) momgod shard2 : 27018(rs2) mongod config1 : 27030 mongos router1 : 27017 |
Node3 | 10.90.9.103 | momgod shard1 : 27018(rs1) momgod shard2 : 27018(rs2) mongod config1 : 27030 mongos router1 : 27017 |
副本集和分片聯合部署的基本思路是先建立副本集,然後將每個副本集作為整體建立分片,如在上表中,叢集有兩個副本集 rs1 和 rs2,每個副本集由三個成員組成,分別部署在三臺機器 Node1、Node2 和 Node3 上。
每個副本集作為一個整體建立一個分片,因此,此叢集由兩個 Shard Server 組成,每個 Shard Server 由一個三成員的副本集來保證資料的容錯和冗餘。
另外,在每臺機器上啟動一個 mongod 和 mongos 例項分別用於實現 Config Server 和 Route Server 的功能,使用三臺機器備份的方式保證叢集的可靠性。
部署副本集
標準副本集一般會部署三個成員,即一個 Primary 和兩個 Secondary,實現資料的冗餘和容錯。 以下步驟為配置表 1 叢集中的副本集 rs1、rs2 的部署。
啟動副本集
- 修改配置檔案
在表 1 所示的叢集中,副本 rs1 在三個節點上均啟動了一個 mongod 例項來完成資料的儲存,啟動 mongod 例項前先修改配置檔案 /etc/mongodrs1.conf,主要是對 path、dbpath、port 這幾項的修改,如下所示:
systemLog:
path: /root/mongodb/data/mongodrsl.log #副本集rs1的日誌檔案位置
storage:
dbPath: /root/mongodb/data/rsl #副本集rs1的資料庫儲存位置
net:
port: 27018 #副本集rs1 mongod程序使用的埠號。
bindIp: 10.90.9.101 #即本機地址,允許mongo客戶端連線。
在 /root/mongodb 資料夾下建立 /data/rs1 目錄,用來儲存副本集rsl的資料庫和日誌檔案。
- 啟動 mongod 副本集
在 Nodel 機器上啟動 mongod 程序為副本集模式,在 Shell 終端執行:
mongod --shardsvr --replSet rs1 --config /etc/mongodrs1.conf
引數說明:
- --shardsvr 表示本叢集中的資料庫是可分片的。
- --replSet 用於指定副本集名稱。
- --config 用於指定配置檔案位置。
在 Nodel 機器上啟動副本集為 rs1 的 MongoDB 例項,如下圖所示。
同樣地,在 Node2 和 Node3 節點上以同樣的方式啟動 mongod 服務,在此階段,三臺機器上的副本整合員都是 Secondary 節點,只有經過初始化才會稱為 Primary 節點。
- 初始化副本集
啟動 MongoDB 的副本集服務後,可在任意一臺機器上連線 MongoDB 的服務,例如,在 Nodel 節點上執行以下命令連線 Node2 節點上的 mongod 服務。
mongo --host 10.90.9.102 --port 27018
引數說明:
- --host 表示需連線的節點 IP。
- --port 是啟動 mongod 服務的埠,埠號在 /etc/mongod.conf 配置檔案中配置。
此命令執行後進入 mongo shell 的互動介面,如圖下所示。
然後在此介面使用 rs.initiate() 對副本集進行初始化,經過初始化後,執行 rs.status。
檢視副本集狀態,如下圖所示,初始化後的 Node2 已經作為副本集 rs1 的 Primary 節點。
- 新增成員
目前 rs1 副本集還只有 Node2 這個 Primary 節點,需要將 Node1、Node3 節點新增到副本集中,執行以下命令新增副本整合員:
rs.add( "10.90.9.101 : 27018" )
rs.add( "10.90.9.103 : 27018" )
至此副本集的啟動配置已完成,通過 rs.stutas() 命令可以看到 rs1 副本集中已經有一個 Primary 節點(10.90.9.102)和兩個 Secondary 節點(10.90.9.101 和 10.90.9.103)。副本集 rs2 以同樣的步驟部署即可。
測試副本集複製功能
- 在 Primary 節點上新增資料
在 Primary 節點上建立 myDB 資料庫,在此資料庫中建立集合 myCollection,並插入 5 個文件,如圖下所示。
- 在 Secondary 節點上檢視副本資料
使用 mongo 命令連線 Secondary 節點,Secondary 節點上的資料預設是不允許讀寫的,可以通過以下命令設定副本節點允許查詢。
db.getMongo().setSlaveOk()
然後查詢 Secondary 節點上的資料,查詢結果如下圖所示。
- 管理副本集
通過 rs.conflg() 命令可以檢視副本集中每個成員的屬性,如下圖所示。
修改副本集屬性可通過如下命令實現:
con=rs.conf()
con.members[1].priority=2
rs.reconfig(con)
首先定義物件 con,將副本集的配置資訊賦給 con,之後將 con 成員列表中的第 2 個(編號從 0 開始)成員的優先順序設為 2,最後以 con 為引數,使用 rs.reconfig() 函式對副本集屬性進行重設。
在 MongoDB 中只能通過主節點將 Mongo 服務新增到副本集中,可以使用命令 db.isMaster() 判斷當前執行的 Mongo 服務是否為主節點,其他副本集的操作可檢視 rs.help 來了解。
部署分片叢集
分片叢集由配置伺服器、路由伺服器、分片伺服器和客戶端組成。客戶端可以是 Shell 終端,也可以是具體的應用程式。
配置伺服器(Config Server)是普通的 mongod 伺服器,儲存著叢集的配置資訊:叢集中有哪些分片、分片的是哪些集合,以及資料塊的分佈。分片伺服器(Shard Server)儲存具體的分片資料。啟動集群后,路由伺服器(Route Server)載入 Config Server 中的分片資訊, 客戶端通過連線 Route Server 來獲取叢集中的資料資訊。
啟動分片機制
在表 1 的分散式叢集中,有兩個分片,分別由副本集 rs1、rs2 組成。叢集中的 Shard Server 已經在前面配置好,接下來需要建立 Config Server 和 Route Server。
- 配置 Config Server
Config Server 相當於叢集的大腦,儲存著叢集和分片的元資料,即各分片包含哪些資料的資訊。鑑於它所包含資料的極端重要性,必須啟用其日誌功能,並確保其資料儲存在非易失性驅動器上。
因此,在叢集中,Config Server 也通常配置成副本集模式來保證資料的可靠性。由於 mongos 需從配置伺服器獲取配置資訊,因此配置伺服器應先於任何 mongos 程序啟動。
配置伺服器是獨立的 mongod 程序,所以可以像啟動“普通的”mongod 程序一樣啟動配置伺服器:
mongod --replSet config --configsvr --dbpath /home/ubuntu/mongodb/data/config --port 27030 -logpath /home/ubuntu/mongodb/data/config.log --logappend --fork
分別在三臺機器上執行以上命令來啟動配置伺服器,使用 replSet config 選項,表示該例項歸屬於名為 config 的副本集。
引數說明:
- --configsvr:選項表明啟動的為 config server,埠為 27030。
- --dbpath:表示資料儲存路徑
- --logpath:表示日誌檔案路徑。
配置 config 副本集的過程請參考本節前面的內容。
使用副本集選項實現了配置資訊的冗餘儲存。配置伺服器並不需要太多的空間和資源。配置伺服器的 1KB 空間約等於 200MB 真實資料,它儲存的只是資料的分佈表。
由於配置伺服器並不需要太多的資源,因此可將其部署在執行著其他程式的機器上,如應用伺服器、分片的 mongod 伺服器或 mongos 程序的伺服器上。
- 配置 Route Server
三個配置伺服器均處於執行狀態後,啟動一個 mongos 程序供應用程式連線。因為 mongos 程序需要知道配置伺服器的地址,所以必須使用 --configdb 選項啟動 mongos:
mongos --configdb config/10.90.9.101:27030,10.90.9.102:27030,10.90.9.103:27030 -logpath /home/ubuntu/mongodb/data/mongos.log --logappend --fork
在預設情況下,mongos 執行在 27017 埠。注意,並不需要指定資料目錄(mongos 自身並不儲存資料,它會在啟動時從配置伺服器載入叢集資料)。確保正確設定了 logpath,以便將 mongos 日誌儲存到安全的地方。
可啟動任意數量的 mongos 程序,通常的設定是每個應用程式伺服器使用一個 mongos 程序(與應用伺服器執行在同一臺機器上)。每個 mongos 程序必須按照列表順序,使用相同的配置伺服器列表,如 --configdb 後面輸入的是一個帶有三個伺服器列表的 config 的副本集的名稱。
至此,分散式叢集中的分片服務已經啟動完畢,接下來進行分片伺服器的設定和資料的分片儲存。
測試分片機制
- 新增分片
為了將副本集轉換為分片,需告知 mongos 副本集名稱和副本整合員列表。例如,在 Node1、 Node2 和 Node3上有一個名為 rs1 和 rs2 的副本集,將每個副本集作為一個分片。
首先執行以下命令連線 mongos:
mongo --host 10.90.9.101 --port 27017
然後進入 mongos 的 Shell 介面,執行下面兩條命令,將兩個副本集 rs1 和 rs2 加入分片集中:
db.runCommand ({ addshard : "rs1/10.90.9.102:27020,10.90.9.101:27020,10.90.9.103 : 27020", name : "s1", maxsize : 10240 });
db.runCommand ({ addshard : "rs2/10.90.9.102:27021,10.90.9.101:27021,10.90.9.103 : 27021", name : "s2", maxsize : 1024 });
可在引數中指定副本集的所有成員,但並非一定要這樣做。mongos 能夠自動檢測到沒有包含在副本整合員表中的成員。name 選項表示此分片的名稱,maxsize 選項指定此分片的最大儲存容量。
如果之後需要移除這個分片或是向這個分片遷移資料,可使用分片名稱標識這個分片。這比使用特定的伺服器名稱要好,因為副本整合員和狀態是不斷改變的。
將副本集作為分片新增到集群后,就可以將應用程式設定從連線到副本集改為連線到 mongos。新增分片後,mongos 會將副本集內的所有資料庫註冊為分片的資料庫,因此,所有查詢都會被髮送到新的分片上。與客戶端庫相同,mongos 會自動處理應用故障,將錯誤返回客戶端。
使用者也可以建立單 mongod 伺服器的分片(而不是副本集分片),但不建議在生產中使用。直接在 addShard() 中指定單個 mongod 的主機名和埠,就可以將其新增為分片了 :
sh.addShard("some-server:port")
單一伺服器分片預設會被命名為 shard0000、shard0001 …… 以此類推。
如打算以後切換為副本集,應先建立一個單成員副本集再新增為分片,而不是直接將單一伺服器新增為分片。如需將單一伺服器分片轉換為副本集,則需停機進行操作。
- 資料分片
除非明確指定規則,否則 MongoDB 不會自動對資料進行拆分。如有必要,必須明確告知資料庫和集合。
假設希望對 myDB 資料庫中的 Mytest 集合按照 _id 鍵進行分片。首先對 myDB 資料庫執行以下命令啟用分片:
sh.enableSharding("myDB")
命令執行成功後,用 sh.status() 查詢分片狀態,如下圖所示,資料庫 myDB 的 patitioned 屬性值為true。
對資料庫分片是對集合分片的先決條件。資料庫啟用分片後,就可以使用如下 shardCollection() 命令對集合進行分片了:
sh.shardCollection("myDB.myColletion", {"_id" : 1})
現在,集合會按照 _id 鍵進行分片。如果是對已存在的集合進行分片,則 _id 鍵上必須包含索引,否則 shardCollection() 會返回錯誤。如果出現了錯誤,則先建立索引,然後重試 shardCollection() 命令;如要進行分片的集合不存在,則 mongos 會自動在片鍵上建立索引。
shardCollection() 命令會將集合拆分為多個數據塊,這是 MongoDB 遷移資料的基本單元。
成功執行分片操作後,MongoDB 會均衡地將集合資料分散到叢集的分片上。這個過程不是瞬間完成的,對於比較大的集合,可能需要花費幾個小時才能完成。