歡迎光臨
每天分享高質量文章

MongoDB 分片概念和原理

作者:perfecttshoot

連結:https://blog.csdn.net/wanght89/article/details/77842336

1、什麼是分片

到目前為止,你都是把MongoDB當做一臺伺服器在用,每個mongod實體都包含應用程式資料的完整副本。就算使用了複製,每個副本也都是完整克隆了其他副本的資料。對於大多數應用程式而言,在一臺伺服器上儲存完整資料集是完全可以接受的。但隨著資料量的增長,以及應用程式對讀寫吞吐量的要求越來越高,普通伺服器漸漸顯得捉襟見肘了。尤其是這些伺服器可能無法分配足夠的記憶體,或者沒有足夠的CPU核數來有效處理工作負荷。除此之外,隨著資料量的增長,要在一塊磁碟或者一組RAID陣列上儲存和管理備份如此大規模的資料集也變得不太現實。如果還想繼續使用普通硬體或者虛擬硬體來託管資料庫,那麼這對這類問題的解決方案就是將資料庫分佈到多臺伺服器上,這種方法稱之為分片。

為數眾多的Web應用程式,知名的如Flicker和LiveJournal,都實現了手動分片,將負載分佈到多臺MySQL資料庫上。在這些實現中,分片邏輯都寄生於應用程式上。要明白這是如何實現的,想象一下,假如你有很多使用者,需要將Users表分佈到多臺資料庫伺服器上。你可以指定一臺資料庫作為元資料庫。這臺資料庫包含每個使用者ID(或者使用者ID範圍)到指定分片對映關係的元資料。因此,要查詢一個使用者實際涉及兩次查詢:第一次查詢訪問元資料庫以獲得使用者的分片位置,第二次查詢直接訪問包含使用者資料的分片。

對於這些Web應用程式而言,手動分片解決了負載問題,但氣質並非無懈可擊。最明顯的問題就是遷移資料非常困難。如果單個分片負載過重,將其中的資料遷移到其他分片的過程完全是手動的。手動分片的第二個問題在於編寫可靠的應用程式程式碼對於讀寫請求進行路由,並且將資料庫作為一個整體進行管理,這也是非常困難的。最近也出現了管理手動分片的的框架,最著名的就是Twitter的Gizzard。

但正如那些手動分片資料庫的人所說,要把事情做好並非易事。MongoDB中有一大塊工作就是為瞭解決這個問題。因為分片是MongoDB的核心內容,所以使用者無需擔心在需求水平擴充套件時要自己設計外接分片框架。在處理困難的跨分片資料均衡問題時,這點尤為重要。這些程式碼並非那些大多數人在一個週末能夠寫出來的東西。

也許最值得一提的是MongoDB在設計時為應用程式提供了統一介面,無論是在分片前,還是在分片後。也就是說,在資料庫需要轉換為分片架構時,應用程式幾乎無需改變。

2、何時分片

這個問題的答案比你想的簡單得多。我們之前已經說過把索引和工作資料集放在記憶體裡時很重要的,這也是分片的主要原因。如果應用程式的資料集持續無限增長,那麼遲早一天,記憶體會容納不下這些資料。如果你正在使用亞馬遜的EC2,那麼這個閾值是68GB。或者你可以執行自己的硬體,並使用遠高於68GB的記憶體,這樣便能延後一段時間再做分片。但沒有哪臺機器記憶體時無限的,因此你早晚都會用到分片。

不可否認,還有一些其他的應對措施。舉例來說,如果你有自己的硬體,而且可以將所有的資料都儲存在固態硬碟上,那麼可以增加資料記憶體比,而不會為效能帶來負面影響。還有一種情況,工作集是總資料量中的一部分,這是可以使用相對較小的記憶體。另一方面,如果有特殊的寫負載要求,那麼可以在資料達到記憶體大小之前先進行適當的分片,原因是需要將負載分到多臺機器上,以便能夠獲得想要的吞吐量。

無論哪種情況,對現有系統進行分片的決定都要基於以下幾點–磁碟活動、系統負載以及最重要的工作集大小與可用記憶體的比例。

3、分片的工作原理

要理解分片是如何工作的,你需要瞭解構成分片叢集的元件,理解協調哪些元件的軟體行程。

(1)分片元件

分片叢集由分片、mongos路由器和配置伺服器組成。如下圖所示

MongoDB分片叢集將資料分佈在一個或多個分片上。每個分片部署成一個MongoDB副本集,該副本集儲存了叢集整體資料的一部分。因為每個分片都是一個副本集,所以他們擁有自己的複製機制,能夠自動進行故障轉移。你可以直接連線單個分片,就像連線單獨的副本集一樣。但是,如果連線的副本集是分片叢集的一部分,那麼只能看到部分資料。

mongos路由器

  如果每個分片都包含部分叢集資料,那麼還需要一個介面連線整個叢集。這就是mongos。mongos行程是一個路由器,將所有的讀寫請求指引到合適的分片上。如此一來,mongos為客戶端提供了一個合理的系統檢視。

 mongos行程是輕量級且非持久化的。它們通常執行與與應用伺服器相同的機器上,確保對任意分片的請求只經過一次網路跳轉。換言之,應用程式連線本地的mongos,而mongos管理了指向單獨分片的連線。

配置伺服器

如果mongs行程是非持久化的,那麼必須有地方能持久儲存叢集的公認狀態;這就是配置伺服器的工作,其中持久化了分片叢集的元資料,改資料包括:每個資料庫,集合和特定範圍資料的位置;一份變更記錄,儲存了資料在分片之間進行遷移的歷史資訊。配置伺服器中儲存的元資料是某些特定功能和叢集維護是的重中之重。舉例來說,每次有mongos行程啟動,它都會從配置伺服器中獲取一份元資料的副本。沒有這些資料,就無法獲得一致的分片叢集檢視。該資料的重要性對配置伺服器的設計和部署也有影響。

如上面結構圖中所示,有三個配置伺服器,但它們並不是以副本集的形式部署的。它們比非同步複製要求更嚴格;mongos行程向配置伺服器寫入時,會使用兩階段提交。這能保證配置伺服器之間的一致性。在各種生產環境的分片部署中,必須執行三個配置伺服器,這些伺服器都必須部署在獨立的機器上以實現冗餘。

(2) 核心分片操作

MongoDB分片叢集在兩個級別上分佈資料。較粗的是以資料庫為粒度的,在集群裡新建資料庫時,每個資料庫都會被分配到不同的分片裡。如果不進行別的設定,資料庫以及其中的集合永遠都會在建立它的分片裡。因為大多數的應用程式都會把所有的資料儲存在一個資料庫裡,因此這種分佈方式帶來的幫助不大。你需要更細粒度的分佈方式,集合的粒度剛好能夠滿足要求。MongoDB的分片是專門為了將單獨的集合分佈在多個分片裡而設計的。

假設你正在構建一套基於雲的辦公軟體,用於管理電子錶格,並且要求將所有的資料都儲存在MongoDB裡。使用者可以隨心所欲地建立大量檔案,每個檔案都會儲存為單獨的MongoDB檔案,放在一個spreadsheets集合裡。隨著時間的流逝,假設你的應用程式發展到了擁有100萬使用者。現在再想想那兩個主要集合:users和spreadsheets。users集合還比較容易處理,就算有100萬使用者,每個使用者檔案1KB,整個集合大概也就1GB,一臺機器就搞定了。但spreadsheets集合就大不一樣了,假設每個使用者平均擁有50張電子錶格,平均大小是50KB,那麼我們所談論的就是1TB的spreadsheets集合。要是這個應用程式的活躍度很高,你會希望將資料放在記憶體裡,要將資料放在記憶體裡並且分佈讀寫負載,就必須將集合分片。

分片一個集合

MongoDB的分片是基於範圍的。也就是說分片集合裡的每個檔案都必須落在指定鍵的某個值範圍裡。MongoDB使用所謂的分片鍵(shard key)讓每個檔案在這些範圍裡找到自己的位置。(其他的分散式資料庫裡可能使用分割槽鍵partition key或分佈鍵 distribution key來代替分片鍵這個術語)從假想的電子錶格管理應用程式裡拿出一個實體檔案,這樣能更好地理解分片鍵:

{
_id:ObjectId("4d6e9b89b600c2c196442c21")
filename:"spreadsheet-1"
updated_at:ISODate("2017-09-04T19:22:54.845z")
username:"banks"
data:"raw documnet data"
}

在對該集合進行分片時,必須將其中的一個或多個欄位申明為分片鍵。如果選擇_id,那麼檔案會基於物件ID的範圍進行分佈。但是,處於一些原因,你要基於username和_id宣告一個複合分片鍵:因此,這些範圍通常會表示Wie一系列使用者名稱
  現在你需要理解塊(chunk)的概念,它是位於一個分片中的一段連續的分片鍵範圍。舉例來說,可以假設docs集合分佈在兩個分片A和B上,它被分成下表所示的多個塊。每個塊的範圍都由起始值和終止值來標識。

起始值 終止值 分片
-無窮大 abbot B
abbot dayton A
dayton harris B
harris norris A
norris 無窮大 B

粗略掃視上表後,你會發現一個重要的、有些違反直覺的屬性:雖然每個單獨的塊都表示一段連續範圍的資料,但這些塊能出現在任意分片上。關於塊,第二個要點是它們是種邏輯上的東西,而非物理上的。換言之,塊並不表示磁碟上連續的檔案。從一定程度上來說,如果一個從harris開始到norris結束的塊存在於分片A上,那麼就認為可以在分片A的docs集合裡找到分片鍵落在這個範圍內的檔案。這個集合裡那些檔案的排列沒有任何必然關係。

拆分與遷移
分片機制的重點是塊的拆分(spliting)與遷移(migration)

首先,考慮一下塊拆分的思想。在初始化分片叢集時,只存在一個塊,這個塊的範圍涵蓋了整個分片集合。那該如何發展到有多個塊的分片叢集呢?答案就是塊大小達到某個閾值是就會對塊進行拆分。預設的塊的最大塊尺寸時64MB或者100000個檔案,先達到哪個標準就以哪個標準為準。在向新的分片叢集新增資料時,原始的塊最終會達到某個閾值,觸發塊的拆分。這是一個簡單的操作,基本就是把原來的範圍一分為二,這樣就有兩個塊,每個塊都有相同數量的檔案。

 請註意,塊的拆分是個邏輯操作。當MongoDB進行塊拆分時,它只是修改塊的元資料就能讓一個塊變為兩個。因此,拆分一個塊並不影響分片集合裡檔案的物理順序。也就是說拆分既簡單又快捷。

你可以回想一下,設計分片系統時最大的一個困難就是保證資料始終均勻分佈。MongoDB的分片叢集是透過在分片中移動塊來實現均衡的。我們稱之為遷移,這是一個真實的物理操作。

遷移是由名為均衡器(balancer)的軟體行程管理的,它的任務就是確保資料在各個分片中保持均勻變化。透過追蹤各分片上塊的數量,就能實現這個功能。雖然均衡的觸發會隨總資料量的不同而變化,但是通常來說,當叢集中擁有塊最多的分片與擁有塊最少的分片的塊數相差大於8時,均衡器就會發起一次均衡處理。在均衡過程中,塊會從塊較多的分片遷移到塊較少非分片上,直到兩個分片的塊數大致相等為止。

已同步到看一看
贊(0)

分享創造快樂