MongoDB

2023-01-07 12:00:37

簡介

什麼是分片

高資料量和高吞吐量的資料庫應用會對單機的效能造成較大壓力,大的查詢會將單機的 CPU 耗盡,大的資料量對單機的儲存壓力較大,最終會耗盡系統的記憶體壓力轉移到磁碟 IO 上。

為了解決這些問題,有兩個基本的方法:

  • 垂直擴充套件:增加更多的 CPU 和儲存資源來擴充套件容量
  • 水平擴充套件:將資料集分佈在多個伺服器上

MongoDB 的分片就是水平擴充套件的體現,使用分片減少了每個分片需要處理的請求數。通過水平擴充套件,叢集可以提高自己的儲存容量和吞吐量。

何時分片

通常來說,不宜過早對資料進行分片,這會增加部署的複雜性;也不應該過晚進行分片,因為很難在不停止執行的情況下對超載的系統進行分片。

通常情況下,分片用於以下情況:

  • 增加可用 RAM
  • 增加可用磁碟空間
  • 減少伺服器的負載
  • 處理單個 mongod 無法承受的吞吐量

叢集結構

一個 MongoDB 的分片叢集包含以下元件:

  • Shard: 即分片,資料的真正儲存位置,以 chunk 為單位存資料;分片也可以部署為一個副本集
  • Router: 查詢的路由,提供使用者端和分片之間的介面;MongoDB 提供了 mongos 程序實現
  • Config Servers: 儲存後設資料和設定資料

資料儲存

分片的塊

在一個分片服務內部,MongoDB 會把資料分為塊,每個 chunk 代表這個分片內部的一部分資料。其作用有兩個:

  • splitting: 當一個 chunk 的大小超過設定的 chunk size 時,MongoDB 的後臺程序會將這個 chunk 繼續切分
  • balancing: 在 MongoDB 中,會有一個 balancer 執行緒負責 chunk 的遷移,從而均衡各個分片的負載

塊的大小

在 MongoDB 中,chunk 的分裂和遷移是非常耗費 IO 資源的,並且 chunk 的分裂只會發生在插入和更新時。

對於大塊和小塊的選擇,其實各有優缺點:

  • 小塊:遷移速度快,資料分佈更均勻;資料分裂頻繁,路由節點消耗更多資源
  • 大塊:資料分裂少,資料塊移動集中消耗 IO 資源

分裂和遷移

隨著資料的增長,其中的資料大小超過了設定的 chunk size(預設 64M),則這個 chunk 就會分裂成兩個。

資料增長的速度快慢會影響 chunk 分裂的速度,資料增長越快則 chunk 分裂的速度越快。

需要注意的是,如果分片試圖分裂的時候,其中一個設定伺服器停止執行了,那麼將無法更新後設資料,則會出現分片一直嘗試拆分塊並一直失敗,這種一直無法成功的過程最終會導致 拆分風暴

一旦發生了分裂,比如說 Shard A 分裂成 3 個塊,Shard B 分裂成 3 個塊,而 Shard C 仍然只有 1 個塊,則各個分片上的 chunk 數量會不平衡,。

這時候,mongos 中的 balancer 執行緒就會執行自動平衡,把 chunk 從 chunk 數量最多的分片挪動到 chunk 數量最少的節點。

如何分片

分片鍵

在對集合進行分片的時,需要選擇一個或多個組合欄位來對資料進行拆分,這個鍵(這些鍵)被稱為分片鍵。

選擇分片鍵非常重要,分片鍵的有以下注意事項:

  • 分片鍵是不可變的
  • 分片鍵必須是索引
  • 分片鍵不能是陣列
  • 分片鍵大小限制 512bytes
  • 分片鍵用於路由查詢
  • 分片鍵的組合最好具有很高的基數

雜湊分片

分片過程中可以使用雜湊索引作為分片鍵,其最大的好處是能保證資料在各個節點分佈基本均勻。

對於基於雜湊的分片,MongoDB 計算一個欄位的雜湊值,並用這個雜湊值來建立資料塊。

在使用基於雜湊分片的系統中,擁有相近分片鍵的檔案很可能不會儲存在同一個資料塊中,資料的分離性更好一些。

基於雜湊分片可以很好地在叢集中分配負載,但是,如果隨機存取超出了 RAM 大小的資料時,效率會比較低。

範圍分片

對於基於範圍的分片,MongoDB 按照分片鍵的範圍把資料分成不同部分。

在使用分片鍵做範圍劃分的系統中,擁有相近分片鍵的檔案很可能儲存在同一個資料塊中,因此也會儲存在同一個分片中。

如果這個分片鍵是一個自增的值時,將會使 MongoDB 難以保持塊的均衡,因為 MongoDB 需要不斷將最後一個分片的資料塊移動到其他分片上。

雜湊和範圍的結合

雜湊分片更適合隨機存取,不適合範圍查詢;範圍分片則是適合範圍查詢,不適合平衡負載。

一個自定義的方案是,對自增欄位構建雜湊索引(儘可能是仍然保持有序的雜湊演演算法)即可解決。