帶你快速瞭解 MongoDB 分佈式集羣

在分佈式應用系統中,mongodb 已經成爲 NoSQL 經典數據庫。要想很好的使用 mongodb,僅僅知道如何使用它是不夠的。只有對其架構原理等有了充分認識,才能在實際運用中使其更好地服務於應用,遇到問題知道怎麼處理,而不是抓瞎抹黑。這篇文章就帶你進入 mongodb 集羣的大門。git

集羣概覽

mongodb 相關的進程分爲三類:github

  • mongo 進程 – 該進程是 mongodb 提供的 shell 客戶端進程,經過該客戶端能夠發送命令並操做集羣;
  • mongos 進程 – mongodb 的路由進程,負責與客戶端鏈接,轉發客戶端請求到後端集羣,對客戶端屏蔽集羣內部結構;
  • mongod 進程 – 提供數據讀寫的 mongodb 實例進程。

類比銀行服務,mongo 進程至關於客戶,mongos 進程是櫃檯服務員,mongod 進程是銀行後臺實際處理業務的人員或者流程。客戶只須要和櫃檯服務員溝通,告知辦什麼業務,櫃檯服務員將業務轉日後臺,後臺實際處理。web

下圖是 mongodb 集羣的通常拓撲結構。算法

如圖,mongodb 集羣的節點分爲三類:mongodb

  • mongos 路由節點:處理客戶端的鏈接,扮演存取路由器的角色,將請求分發到正確的數據節點上,對客戶端屏蔽分佈式的概念;
  • config 配置節點:配置服務,保存數據結構的元數據,好比每一個分片上的數據範圍,數據塊列表等。配置節點也是 mongod 進程,只是它存儲的數據是集羣相關的元數據;
  • shard 分片節點:數據存儲節點,分片節點由若干個副本集組成,每一個副本集存儲部分全體數據,全部副本集的數據組成全體數據,而副本集內部節點存放相同的數據,作數據備份與高可用。

仍是拿銀行業務類比,當客戶辦理保單保存業務時,shell

  1. 櫃檯服務員接受客戶的保單業務請求(mongos 路由節點接收客戶端的操做請求);
  2. 櫃檯服務員查詢文件目錄系統查看該保單應該保存到哪一個倉庫(mongos 節點與 config 配置節點通訊,查詢相關操做數據在哪一個分片節點);
  3. 知道哪一個倉庫後,櫃檯服務員將保單給倉庫管理員,倉庫管理員將保單放到指定倉庫中(mongos 節點將請求發送給數據所在分片節點,分片節點進行讀寫處理)。

mongos 路由服務

mongos 服務相似網關,鏈接 mongodb 集羣與應用程序,對外屏蔽 mongodb 內部結構,應用程序只須要將請求發送給 mongos,而無需關心集羣內部副本分片等信息。數據庫

mongos 自己不保存數據與索引信息,它經過查詢 config 配置服務來獲取,因此能夠考慮將 mongos 與應用程序部署在同一臺服務器上,當服務器宕機時 mongos 也一塊兒失效,防止出現 mongos 閒置。後端

mongos 節點也能夠是單個節點,但爲了高可用,通常部署多個節點。就像櫃檯服務員同樣,能夠有多個,相互之間沒有主備關係,均可以獨立處理業務。bash

須要注意的是,在開啓分片的狀況下,應用程序應該避免直接鏈接分片節點進行數據修改,由於這種狀況下極可能形成數據不一致等嚴重後果,而是經過 mongos 節點來操做。服務器

config 配置服務

config 配置節點本質也是一個副本集,副本集中存放集羣的元數據,如各個分片上的數據塊列表,數據範圍,身份驗證等信息。以下,能夠看到數據庫 config,數據庫中集合保存了集羣的重要元數據。

mongos> use config;
switched to db config
mongos> show collections;
changelog
chunks
collections
databases
lockpings
locks
migrations
mongos
shards
tags
transactions
version

  

通常狀況下,用戶不該該直接變動 config 的數據,不然極可能形成嚴重後果。

shard 分片服務

分佈式存儲要解決的是兩個問題:

  • 隨着業務不斷髮展,數據量愈來愈大,單機存儲受限於物理條件,必然要經過增長服務器來支持不斷增大的數據。因此分佈式下,不可能所有數據存儲在一個節點上,必然是將數據劃分,部分數據放到這個節點,另外部分數據放到另外的節點上。也就是數據的伸縮性。
  • 考慮高可用。若是同一份數據只存在一個節點上,當這個節點發生異常時,數據不可用。這就要求分佈式下同一份數據須要存儲在多個節點上,以達高可用效果。

在 mongodb 集羣中,數據的伸縮性經過分片集來實現,高可用經過副本集來實現。

如圖,所有數據爲1-6,將其劃分爲3部分,1-2爲一個分片,3-4爲一個分片,5-6爲一個分片。每一個分片存儲在不一樣的節點上。而每一個分片有3個副本,組成副本集,每一個副本都是獨立的 mongod 實例。

因此副本集是一個縱向概念,描述的是相同的數據存儲在多個節點上;而分片是一個橫向概念,描述的是全量數據被切成不一樣的片斷,每一個片斷獨立存儲。這個片斷就是分片,而分片經過副本集進行存儲。

副本集

副本集包含三種角色:

  • 主節點(Primary)
  • 副節點(Secondary)
  • 仲裁節點(Arbiter)

一個副本集由一個主節點,多個副節點,0或多個仲裁節點組成。

主節點與副節點是數據節點。主節點提供數據的寫操做,數據寫到主節點後,會經過同步機制同步到副節點上。默認讀操做也由主節點提供,可是能夠手動設置 read preference,優先從副節點讀取。

仲裁節點不是數據節點,不存儲數據,也不提供讀寫操做。仲裁節點是做爲投票者存在,當主節點異常須要進行切換時,仲裁節點有投票權,但沒有被投票權。仲裁節點能夠在資源有限的狀況下,依然支持故障恢復。好比只有2個節點的硬盤資源,在這種狀況下能夠增長一個不佔存儲的仲裁節點,組成「一主一副一仲裁」的副本集架構,當主節點宕掉時,副節點可以自動切換。

節點間經過「心跳」進行溝通,以此知道彼此的狀態。當主節點異常不可用時,從其餘有被投票權的節點中投票選出一個升級爲主節點,繼續保持服務高可用。這裏投票採起「大多數」原則,即須要多於總節點數一半的節點贊成,才能被選舉成主節點。也所以不建議採用偶數個節點組成副本集,由於偶數狀況下,若是發生半數節點網絡隔離,隔離的半數節點達不到「大多數」的要求,沒法選舉產生新的主節點。

經過 rs.status() 能夠查看副本集,參考《教你快速搭建 mongodb 集羣》

分片集

分片就是將所有數據根據必定規則劃分紅沒有交集的數據子集,每一個子集就是一個分片,不一樣分片存放在不一樣節點上。這裏有幾個問題:

  • 劃分規則也就是分片策略是什麼?
  • 分片數據是如何存放的?
  • 數據量愈來愈大,分片如何動態調整?

數據塊 Chunk

chunk 由多個文檔組成,一個分片中包含多個 chunk。chunk 是分片間數據遷移的最小單位。實際上,文檔是經過分片策略計算出應該存儲在哪一個 chunk,而 chunk 存放在分片上。

如圖,假設按照文檔的 x 字段值來進行分片,根據不一樣取值範圍存放在不一樣的數據塊,如25-175在 chunk 3上。

把書比做 mongodb 中的文檔,書櫃比做數據塊,房間比做分片。每本書根據必定規則放到某書櫃上,房間中有不少書櫃。當某個房間的書櫃太多,就須要以書櫃爲單位,遷移到相對比較寬鬆的房間。

chunk 的大小默認爲 64MB,也能夠自定義。chunk 的存在有兩個意義:

  • 當某個 chunk 超過大小時,會觸發 chunk 分裂。
  • 當分片間的 chunk 數不均衡時,會觸發 chunk 遷移。

chunk 遷移由 mongodb 的平衡器來操做,默認平衡器是開啓的,是運行在後臺的一個進程,也能夠手動關閉。

能夠經過下面命令來查看平衡器狀態:

sh.getBalancerState()

chunk 的大小對集羣的影響:

  • 比較小時,chunk 數比較多,數據分佈比較均勻,但會引發頻繁的數據塊分裂與遷移;
  • 比較大時,chunk 數比較少,數據容易分散不均勻,遷移時網絡傳輸量大。

因此要自定義數據塊大小時,必定要考慮完備,不然將大大影響集羣與應用程序的性能。

片鍵 Shard Key

mongodb 集羣不會自動將數據進行分片,須要客戶端告知 mongodb 哪些數據須要進行分片,分片的規則是什麼。

某個數據庫啓用分片:

mongos> sh.enableSharding(<database>)

設置集合的分片規則:

mongos> sh.shardCollection(<database.collection>,<key>,<unique>,<options>)
# unique 與 options 爲可選參數

例如,將數據庫 mustone 開啓分片,並設置庫中 myuser 集合的文檔根據 _id 字段的散列值來進行劃分分片。

sh.enableSharding("mustone")
sh.shardCollection("mustone.myuser",{_id: "hashed"})

這裏劃分規則體如今 上, 定義了分片策略,分片策略由片鍵 Shard Key 與分片算法組成。片鍵就是文檔的某一個字段,也能夠是複合字段。分片算法分爲兩種:

  • 基於範圍。如 設置爲 id:1 表示基於字段 id 的升序進行分片,id:-1 表示基於字段 id 的倒序進行分片,字段 id 就是 shard key(片鍵)。當集合中文檔爲空時,設置分片後,會初始化單個 chunk,chunk 的範圍爲(-∞,+∞)。當不斷往其中插入數據到達 chunk 大小上限後,會進行 chunk 分裂與必要遷移。
  • 基於hash。如上面的栗子, 設置爲 _id:」hashed」,表示根據字段 _id 的哈希來分片,此時片鍵爲 _id。初始化時會根據分片節點數初始化若干個 chunk,如3個分片節點會初始化6個 chunk,每一個 shard 2個 chunk。

每一個數據庫會分配一個 primary shard,初始化的 chunk 或者沒有開啓分片的集合都默認放在這個 primary shard 上。

分片策略的選擇相當重要,等數據量大了再更改分片策略將會很麻煩。分片策略的原則:

  1. 均勻分佈原則。分片的目標就是讓數據在各個分片上均勻分佈,數據的存取壓力也分解到各個分片上。好比以自增加的 id 升序爲片鍵,會致使新數據永遠都寫在最後的 chunk 上,且 chunk 分裂與遷移也會落在該 chunk 所在分片上,形成該分片壓力過大。
  2. 大基數原則。集合的片鍵可能包含的不一樣值的個數,稱爲基數。基數越大,數據就能劃分得更細。基數越小,chunk 的個數就有限。好比性別,只有男女,若是做爲片鍵,最多兩個 chunk,等數據愈來愈大後,便沒法橫向擴展。
  3. 就近原則。儘量讓一次查詢的數據分佈在同一個 chunk 上,這樣提高磁盤讀取性能。避免毫無心義的隨機片鍵,雖然分佈均勻了,但每次查詢都要跨多個 chunk 才能完成,效率低下。

須要說明的是,mongodb 分片集羣雖然比較完備,可是存在一些限制,如備份相對困難,分片集合沒法作關聯查詢等。因此要根據實際業務來評估,若是副本集已經夠用了,不必定要進行分片存取。

相關文章
相關標籤/搜索