深入瞭解MongoDB 分散式叢集
在分散式應用系統中,mongodb 已經成為 NoSQL 經典資料庫。要想很好的使用 mongodb,僅僅知道如何使用它是不夠的。只有對其架構原理等有了充分認識,才能在實際運用中使其更好地服務於應用,遇到問題知道怎麼處理,而不是抓瞎抹黑。這篇文章就帶你進入 mongodb 叢集的大門。
叢集概覽
mongodb 相關的程序分為三類:
- mongo 程序 – 該程序是 mongodb 提供的 shell 客戶端程序,通過該客戶端可以傳送命令並操作叢集;
- mongos 程序 – mongodb 的路由程序,負責與客戶端連線,轉發客戶端請求到後端叢集,對客戶端遮蔽叢集內部結構;
- mongod 程序 – 提供資料讀寫的 mongodb 例項程序。
類比銀行服務,mongo 程序相當於客戶,mongos 程序是櫃檯服務員,mongod 程序是銀行後臺實際處理業務的人員或者流程。客戶只需要和櫃檯服務員溝通,告知辦什麼業務,櫃檯服務員將業務轉往後臺,後臺實際處理。
下圖是 mongodb 叢集的一般拓撲結構。
如圖,mongodb 叢集的節點分為三類:
- mongos 路由節點:處理客戶端的連線,扮演存取路由器的角色,將請求分發到正確的資料節點上,對客戶端遮蔽分散式的概念;
- config 配置節點:配置服務,儲存資料結構的元資料,比如每個分片上的資料範圍,資料塊列表等。配置節點也是 mongod 程序,只是它儲存的資料是叢集相關的元資料;
- shard 分片節點:資料儲存節點,分片節點由若干個副本集組成,每個副本集儲存部分全體資料,所有副本集的資料組成全體資料,而副本集內部節點存放相同的資料,做資料備份與高可用。
還是拿銀行業務類比,當客戶辦理保單儲存業務時,
- 櫃檯服務員接受客戶的保單業務請求(mongos 路由節點接收客戶端的操作請求);
- 櫃檯服務員查詢檔案目錄系統檢視該保單應該儲存到哪個倉庫(mongos 節點與 config 配置節點通訊,查詢相關操作資料在哪個分片節點);
- 知道哪個倉庫後,櫃檯服務員將保單給倉庫管理員,倉庫管理員將保單放到指定倉庫中(mongos 節點將請求傳送給資料所在分片節點,分片節點進行讀寫處理)。
mongos 路由服務
mongos 服務類似閘道器,連線 mongodb 叢集與應用程式,對外遮蔽 mongodb 內部結構,應用程式只需要將請求傳送給 mongos,而無需關心叢集內部副本分片等資訊。
mongos 本身不儲存資料與索引資訊,它通過查詢 config 配置服務來獲取,所以可以考慮將 mongos 與應用程式部署在同一臺伺服器上,當伺服器宕機時 mongos 也一起失效,防止出現 mongos 閒置。
mongos 節點也可以是單個節點,但為了高可用,一般部署多個節點。就像櫃檯服務員一樣,可以有多個,相互之間沒有主備關係,都可以獨立處理業務。
需要注意的是,在開啟分片的情況下,應用程式應該避免直接連線分片節點進行資料修改,因為這種情況下很可能造成資料不一致等嚴重後果,而是通過 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 上。
分片策略的選擇至關重要,等資料量大了再更改分片策略將會很麻煩。分片策略的原則:
- 均勻分佈原則。分片的目標就是讓資料在各個分片上均勻分佈,資料的存取壓力也分解到各個分片上。比如以自增長的 id 升序為片鍵,會導致新資料永遠都寫在最後的 chunk 上,且 chunk 分裂與遷移也會落在該 chunk 所在分片上,造成該分片壓力過大。
- 大基數原則。集合的片鍵可能包含的不同值的個數,稱為基數。基數越大,資料就能劃分得更細。基數越小,chunk 的個數就有限。比如性別,只有男女,如果作為片鍵,最多兩個 chunk,等資料越來越大後,便無法橫向擴充套件。
- 就近原則。儘可能讓一次查詢的資料分佈在同一個 chunk 上,這樣提升磁碟讀取效能。避免毫無意義的隨機片鍵,雖然分佈均勻了,但每次查詢都要跨多個 chunk 才能完成,效率低下。
需要說明的是,mongodb 分片叢集雖然比較完備,但是存在一些限制,如備份相對困難,分片集合無法做關聯查詢等。所以要根據實際業務來評估,如果副本集已經夠用了,不一定要進行分片存取。
以上就是深入瞭解MongoDB 分散式叢集的詳細內容,更多關於MongoDB 分散式叢集的資料請關注我們其它相關文章!