大數據存儲的祕密之分區

分區,又稱爲分片,是解決大數據存儲的常見解決方案,大數據存儲量超過了單節點的存儲上限,因此需要進行分區操作將數據分散存儲在不同節點上,通常每個單個分區可以理解成一個小型的數據庫,儘管數據庫能同時支持多個分區操作;分區引入多分區概念,可以同時對外服務提高性能。

常常和分區一併提及的概念是複製,分區通常與複製結合使⽤,使得每個分區的副本存儲在多個節點上。 這意味着,即使每條記錄屬於⼀個分區,它仍然可以存儲在多個不同的節點上以獲得容錯能⼒。分區在許多技術或框架中都有體現,例如MQ中topic下的分區消息實現,如kafka中的partion、rocketmq中的queue等;例如SQL/NoSQL中分區數據儲存實現,如ElascticSearch中的Shards分片、MySQL中的分表等。

關於分區,本文主要討論下鍵值分區的幾種方式、分區再平衡策略和請求路由處理機制等,最後以ES(ElascticSearch)的查詢請求處理爲例,分析分區下查詢的請求處理流程。話不多說,Let's Go~

鍵值分區的幾種方式

如果有大量數據需要分散存儲,應該如何進行分區呢?分區的目前就是將數據均衡的分散在各節點,這樣同時也能分散對數據的處理請求,如果分區不均衡,那麼會造成某些分區有大量的數據或查詢請求,這就是常說的傾斜。數據傾斜會造成高負載節點形成熱點,避免熱點可以使用隨機路由方式將數據散列到各分區中。對數據進行分區操作,不能僅僅是隨機數據存儲,因爲存儲之後肯定還是要進行查詢的,所以要按照固定鍵值來進行散列分區操作,方便後續查詢請求的路由。常見的鍵值分區方式有按照範圍分區、按照鍵的散列分區:

按照範圍分區

按照範圍分區就是每個分區存儲指定一段連續的數據,比如按照時間戳來存儲數據,最簡單常見的日誌按照時間分割爲不同的文件;按照編號id來存儲數據,如圖書館書籍陳列,編號連續數據存放在同一個書架上。按照範圍分區有時候會造成分區數據不均衡,比如按照時間戳,可能某段時間內數據比較少而某些時間段數據較多而造成分區不均衡。

鍵值散列分區

由於按照範圍分區容易造成數據負載不均衡問題,所以一般應用場景下(非順序類型數據)爲了避免偏斜和熱點的⻛險,會使⽤散列函數來確定給定鍵的分區。一個好的散列函數會盡量隨機分區,許多語言內都內置了散列函數,但是有些可能不太適合分區場景,比如Java的 Object.hashCode()和Ruby的 Object#hash,其同⼀個鍵可能在不同的進程中有不同的哈希值。
有了合適的散列函數,有時候想要讓一定散列範圍內的數據分佈在同一分區,此時可使用一致性哈希,一致性哈希可減小因爲分區變動造成會已有數據分區映射的影響。

熱點問題

哈希分區可幫助減少熱點,但是無法避免,極端情況下可能存在所有請求都打到同一分區中。熱點分區問題解決思路是:一種是給熱點分區再分區操作,比如針對熱點數據的key再路由分散多個分區中;還有一種是熱點數據增加冗餘(也就是複製),增加熱點數據的複製節點,一同對外提供服務。

分區再平衡

隨着時間的推移,分區數據會有以下變化:

  • 查詢吞吐量增加,所以您想要添加更多的CPU來處理負載。
  • 數據集⼤⼩增加,所以您想添加更多的磁盤和RAM來存儲它。
  • 機器出現故障,其他機器需要接管故障機器的責任。

所有這些更改都需要數據和請求從⼀個節點移動到另⼀個節點。 將負載從集羣中的⼀個節點向另⼀個節點移動的過程稱爲再平衡(reblancing),再平衡過程一般要求如下:再平衡之後數據儘量均衡、在平衡時分區要正常地外提供服務、節點之間只移動必要數據以加快再平衡進度。(一般來說直接使用取餘方式散列的分區再平衡時大都需要將所有數據重新取餘再分區,成本較大。)

固定數目的分區

爲了避免分區的擴容再平衡操作,可以創建⽐節點更多的分區,併爲每個節點分配多個分區。例如,運⾏在10個節點的集羣上的數據庫可能會從⼀開始就被拆分爲1000個分區,因此⼤約有100個分區被分配給每個節點。比如ES就是用了這種再平衡方式,ES中的shards分片在運行時是無法更改的,因此生產環境一般會建議針對分區數設定留一定的餘量,方便後續擴容操作。這樣的話,分區的數量不會變化,知識分區數據會在節點間移動而已,鍵所指定的分區也不會改變。唯⼀改變的是分區所在的節點。這種變更並不是即時的,在⽹絡上傳輸⼤量的數據需要⼀些時間,所以在傳輸過程中,原有分區仍然會接受讀寫操作。如下圖所示:

動態分區

對於使用鍵範圍場景來說,具有固定邊界的固定數量的分區將⾮常不便:如果出現邊界錯誤,則可能會導致⼀個分區中的所有數據或者其他分區中的所有數據爲空。⼿動重新配置分區邊界將⾮常繁瑣。因此,按鍵範圍進行分區的數據庫(如HBase和RethinkDB)會動態創建分區。當分區增⻓
到超過配置的⼤⼩時(在HBase上,默認值是10GB),會被分成兩個分區,每個分區約佔⼀半的數據。與之相反,如果⼤量數據被刪除並且分區縮⼩到某個閾值以下,則可以將其與相鄰分區合併,類似B樹的過程類似。
動態分區的⼀個優點是分區數量適應總數據量。如果只有少量的數據,少量的分區就⾜夠了,所以開銷很⼩;如果有⼤量的數據,每個分區的⼤⼩被限制在⼀個可配置的最⼤值,當超過閾值時觸發分區操作。

再平衡操作觸發時,到底應該由人爲觸發還是由程序自動觸發呢?程序自動觸發,一般是檢測節點負載過高或者(通過網絡心跳發現)某個節點掛了,自動再平衡可能因爲某些外界環境的影響就執行了,可能達不到我們的預期,因此,一個合理的方案是,程序自動發現應該執行再平衡時,可以報警通知到運維人員,由人工介入來處理後續的再平衡執行。

請求路由處理

當處理請求時,如何確定哪個節點執行呢?隨着分區再平衡,分區對節點的分配也發生變化,爲了回答這個問題,需要有⼈知曉這些變化:如果我想讀或寫鍵“foo”,需要連接哪個節點IP地址和端⼝號?這個問題本質上就是服務發現,它不僅僅體現在數據庫,任何網絡通信場景都有這個問題,特別是如果它的⽬標是⾼可⽤性(在多臺機器上運⾏冗餘配置),都需要服務發現。概括來說,請求路由處理,有以下幾種處理方案:

  1. 允許客戶聯繫任何節點(例如,通過循環策略的負載均衡(Round-Robin Load Balancer))。如果該節點恰巧擁有請求的分區,則它可以直接處理該請求;否則,它將請求轉發到適當的節點,接收回復並傳遞給客戶端。
  2. ⾸先將所有來⾃客戶端的請求發送到路由層,它決定了應該處理請求的節點,並相應地轉發。此路由層本身不處理任何請求;它僅負責分區的負載均衡。
  3. 要求客戶端知道分區和節點的分配。在這種情況下,客戶端可以直接連接到適當的節點,⽽不需要任何中介代理。

以上所有情況的關鍵問題是,做出路由決策的組件(可能是節點之一、客戶端或者路由代理)如何知道分區-節點之間的映射關係。映射關係可以使固定寫死在代碼中,也可以是配置在配置中心中。許多分佈式數據系統都依賴於⼀個獨⽴的協調服務,⽐如ZooKeeper來跟蹤集羣元數據。 每個節點在ZooKeeper中註冊⾃⼰,ZooKeeper維護分區到節點的可靠映射。 其他參與者(如路由層或分區感知客戶端)可以在ZooKeeper中訂閱此信息。 只要分區分配發⽣的改變,或者集羣中添加或刪除了⼀個節點,ZooKeeper就會通知路由層使路由信息保持最新狀態。

執行查詢

請求處理查詢可分爲兩種場景,單節點查詢和集羣查詢,前者一般是針對一類數據的查詢並且該類數據存儲在同一個節點上,後者是同時發給多個節點,最後再做聚合操作。集羣查詢也稱爲並行查詢,通常⽤於分析的⼤規模並⾏處理(MPP, Massively parallel processing) 關係型數據庫產品在
其⽀持的查詢類型⽅⾯要複雜得多。⼀個典型的數據倉庫查詢包含多個連接,過濾,分組和聚合操作。

ES的查詢處理流程

ES使用開源的Lucene作爲存儲引擎,它賦予ES高性能的數據檢索能力,但Lucene僅僅是一個單機索引庫。ES基於Lucene進行分佈式封裝,以支持集羣管理、分佈式查詢、聚合分析等功能。

從使用的直觀感受看,ES查詢分爲2個階段,query和fetch階段。在query階段會從所有的shard上讀取相關document的docId及相關的排序字段值,並最終在coordinating節點上收集所有的結果數進入一個全局的排序列表後,然後獲取根據from+size指定page頁的數據,獲取這些docId後再構建一個multi-get請求發送相關的shard上從_source裏面獲取需要加載的數據,最終再返回給client端。

query階段:

fetch階段

所有的搜索系統一般都是兩階段查詢,第一階段查詢到匹配的DocID,第二階段再查詢DocID對應的完整文檔,這種在Elasticsearch中稱爲query_then_fetch,還有一種是一階段查詢的時候就返回完整Doc,在Elasticsearch中稱作query_and_fetch,一般第二種適用於只需要查詢一個Shard的請求。由上圖可知,ES允許客戶聯繫任何節點,如果該節點恰巧擁有請求的分區,則它可以直接處理該請求;否則,它將請求轉發到適當的節點,接收回復然後聚合並傳遞最終的聚合結果給客戶端。

小結

大數據量場景在單臺機器上存儲和處理不再可⾏,則分區⼗分必要。分區的⽬標是在多臺機器上均勻分佈數據和查詢負載,避免出現熱點(負載不成⽐例的節點)。這需要選擇適合於您的數據的分區⽅案,並在將節點添加到集羣或從集羣刪除時進⾏再分區。

常見的鍵值分區方式有按照範圍分區、按照鍵的散列分區兩種。請求的處理機制一般有客戶端處理、代理處理、服務節點處理3種方式,不管哪種方式,都需要其知道分區-節點之間的映射關係,一般映射關係是保存在配置中心上,比如zookeeper。

 

推薦閱讀 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章