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