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

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)

分享創造快樂