DDIA笔记—第六章 数据分区
第六章 數據分區
數據分區與數據復制
分區通常與復制結合使用,即每個分區在多個節點都存在副本,這就意味著某條記錄屬于特定的分區,而同樣的內容會保存在不同的節點上以提高系統的容錯性。
每個節點同時充當某些分區的主副本和其他分區的從副本:
如何進行分區?
如何決定哪些記錄放在哪些節點上?分區的主要目的是將數據和查詢負載均勻的分布在所有節點上。如果分區不均勻,則會出現某些分區節點比其他分區承擔更多的數據量和查詢負載,稱之為傾斜。更為嚴重的情況是,所有的負載都集中在一個分區節點上,這種負載嚴重不成比例的分區稱為系統熱點。避免系統熱點最簡單的方式就是將記錄隨機分配給所有節點,但這有帶來了另一個問題:如何讀取數據?(簡單的鍵-值數據模型,可以通過關鍵字來訪問記錄)
鍵-值數據的分區
基于關鍵字區間分區
為每個分區分配一段連續的關鍵字或者關鍵值區間范圍。為了更均勻的分布數據,分區邊界理應適配數據本身的分布特征。
但是基于關鍵字的區間分區的缺點是某些訪問模式可能會導致熱點,例如:采用時間戳作為關鍵字,則分區對應于一個時間范圍,如果將每天作為一個分區,同一天內所有寫入都集中在同一個分區,而其他的分區始終處于空閑狀態。
基于關鍵字哈希值分區
一個好的哈希函數可以處理數據傾斜并使其均勻分布。一旦找到了合適的關鍵字哈希函數,就可以為每個分區分配一個哈希范圍,關鍵字根據其哈希值的范圍劃分到不同的分區中。
這種方式看似非常完美,但是在做范圍查詢時,往往會變的非常麻煩:即使關鍵字相鄰,但經過哈希函數之后可能會被分散到不同的分區中。在MongoDB中,如果啟用了基于哈希的分片模式,則區間查詢會發送到所有分區上。
綜上,基于哈希的分區方法可以減輕熱點,但無法做到完全避免。
分區與二級索引
二級索引帶來的主要挑戰是它們不能規整的映射到分區中。有兩種主要的方法來支持對二級索引進行分區:
基于文檔分區的二級索引
如圖,每條記錄都有唯一的ID,首先用此ID對數據庫進行分區(例如:0 <= ID < 500屬于分區0,500 <= ID < 1000屬于分區1)?,F在用戶需要搜索汽車,可以支持按汽車顏色和廠商進行過濾,所以需要在顏色和制造商上設定二級索引。聲明這些索引之后,數據庫會自動創建索引。
在這種索引方法中,每個分區完全獨立,各自維護自己的二級索引,因此文檔分區索引也被稱為本地索引。
讀取時需要注意:如果是要查詢所有紅色汽車(假設沒有對ID做特殊處理),則查詢請求需要發送到所有的分區,然后再合并結果。所以導致了查詢代價高昂、讀延遲加大。
基于詞條的二級索引分區
另一種方法,我們可以對所有的數據構建全局索引,同時,為了避免瓶頸,不能將全局索引存儲在一個節點上,否則就破壞了設計分區均衡的目標。所以,全局索引也必須分區,且可以與數據關鍵字采用不同的分區策略。
如圖,所有顏色為紅色的汽車的ID收錄在索引color:red中,而索引本身也是分區的,例如從a~r開始的顏色索引放在分區0中。我們將這種索引方案稱為詞條分區。和前面討論的方法一樣,可以直接通過關鍵字來全局劃分索引,或者對其取哈希值,各自的優點在前面也都提到了。
這種全局的詞條索引相比于文檔分區索引的主要優點是:它的讀取更為高效,即不需要向所有分區都查詢一遍。但是缺點也非常明顯:由于需要維護索引,導致它的寫入速度非常慢。理想情況下,索引應該時刻保持最新,但是,對于詞條分區來說,這需要一個跨多個相關分區的分布式事務支持(這也是現有數據庫不支持同步更新二級索引的原因)。
分區再平衡
隨著時間的推移,數據庫可能總會出現,某些變化:
- 查詢壓力增加,因此需要更多的CPU來處理負載;
- 數據規模增加,因此需要更多的磁盤和內存來存儲數據;
- 節點可能出現故障,因此需要其他節點代替失效節點;
所有這些變化都要求數據和請求能從一個節點轉移到另一個節點,這樣一個過程就稱為再平衡(動態平衡)。無論對于哪種分區方案,分區再平衡通常只少要滿足:
- 平衡之后,負載、數據存儲、讀寫請求等應該在集群范圍更均勻的分布;
- 再平衡執行過程中,數據庫應該可以繼續正常提供讀寫服務;
- 避免不必要的負載遷移,并盡量減少網絡和磁盤I/O影響;
動態再平衡策略
為什么不采用模運算?
如果節點數發生變化,將會導致大量的關鍵字需要從現有節點遷移到另一個節點,頻繁的遷移大大增加再平衡的成本。
固定數量的分區
如圖,如果集群中添加了一個新節點,該新節點可以從每個現有的節點上勻走幾個分區,直到分區再次達到全局平衡(當然,如果增加的節點性能更加強大,則可以給它分配更過的分區,從而分擔更多的負載)。刪除的話,就是一個逆向的操作。這種方式不會改變關鍵字到分區的映射關系,唯一要調整的是分區與節點的對應關系。
在這種方式中,為了使得相關操作變得非常簡單,分區的數量往往中數據庫創建時就已經確定好(原則上可以拆分和合并,但是為了簡單,許多數據庫決定不支持分區拆分和合并),所以,在初始化時,就會設置一個足夠大的分區數(每個分區都會有額外的管理開銷)。如果數據集的規模不確定,此時如何選擇合適的分區數以及分區大小就會有些困難。
動態分區
簡單的理解就是,當分區的數據增長超過一個參數閾值(HBase默認值為10GB)時,它就拆分為兩個分區(可以將其中一部分轉移到其他節點),每個分區承擔一半的數據量。相反,如果數據被大量刪除,并且縮小到某個閾值以下,則將其與相鄰分區進行合并。
動態分區的一個優點是:分區數量可以自動適配數據總量。
但對于一個空的數據庫,初始時可能會從一個分區開始,這樣在達到第一個分裂點之前,所有寫入請求都由單個節點來處理,而其他節點處于空閑狀態。為了緩解這種問題,HBase和MongoDB允許采用預分裂(配置一組初始分區),同時,預分裂要求知道一些關鍵字的分布情況。
按節點比例分區
動態分區中分區的數量與數據集的大小成正比,固定數量分區中分區大小也與數據集反大小成正比,這兩種方式都與節點數無關。
一些數據庫系統(Cassandra、Ketama)采用了第三種方式,使分區數與集群節點數成正比。也就是說,每個節點具有固定數量的分區。當一個新節點加入集群時,它隨機選擇固定數量的現有分區進行分裂,然后拿走這些分區的一半數據。
自動與手動再平衡操作
再平衡總體上講是一個昂貴的操作,它需要重新路由請求,并將大量數據從一個節點遷移到另一個節點。
全自動再平衡雖然方便,但有可能在再平衡過程中出現難以預測的情況。例如:假設某個節點負載過重,對請求對響應暫時受到影響,其他的節點可能會得到結論:該節點失效;接著觸發自動平衡來轉移負載,這無疑會加重該節點、其他節點以及網絡的負載。
所以,你怎么選擇,自動 or 手動?
請求路由
客戶端如何知道要連接哪個節點?
這個問題有以下幾種處理策略:
- 允許客戶端鏈接任意節點。如果某節點恰好擁有所請求的分區,則直接處理該請求;否則,將請求轉發給下一個節點,接收答復,并將答復返回給客戶端;
- 所有客戶端的請求發給路由層,由路由層將請求轉發給對應的分區節點;
- 客戶端自己感知分區和節點的分配關系;
所以,這里的核心問題就變成了:作出路由決策的組件(某個節點、路由層、客戶端)是如何知道分區與節點的對應關系以及變化情況的呢?很多分布式數據系統依靠獨立的協調服務(如ZooKeeper)跟蹤集群范圍內的元數據。類似的還有:MongoDB依賴于自己的配置服務器和mongos守護進程來充當路由層等。
每個節點都向Zookeeper中注冊自己,ZooKeeper維護了分區到節點的最終映射關系。其他參與者可以向ZooKeeper訂閱此信息。一旦分區發生了改變,或者刪除、添加節點,ZooKeeper都會通知參與者。
并行查詢執行
大規模并行計算(massively parallel processing,MPP)中查詢類型方面要復雜的多,典型的操作會包含多個聯合、過濾、分組、聚合等操作。MPP查詢優化器會將復雜的查詢分解成許多執行階段和分區,以便在不同節點上并行執行。
總結
以上是生活随笔為你收集整理的DDIA笔记—第六章 数据分区的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何在电脑版快投屏中使用USB有线投屏功
- 下一篇: Excel之Char.Chr.Chrw函