HBase的compact分析,HBasecompact分析
HBase是基於LSM樹儲存模型的分布式NoSQL資料庫。LSM樹對比普遍的B+樹來說,能夠獲得較高隨機寫效能的同時,也能保持可靠的隨機讀效能(可參考這裡)。在進行讀請求的時候,LSM樹要把多個子樹(類似B+樹結構)進行歸併查詢,對於HBase來說,這些子樹就是HFile(還包括記憶體上的樹結構MemStore)。因此歸併查詢的子樹數越少,查詢的效能就越高。
Compact的作用
在寫請求的這篇文章裡,已經介紹過對於每個寫請求,都必須寫入MemStore以及HLog才算完成事務提交。當MemStore超過閥值的時候,就要flush到HDFS上產生一個HFile。因此隨著不斷寫入,HFile的數量將會越來越多,根據前面所述,HFile數量過多會降低讀效能。為了避免對讀效能的影響,可以對這些HFile進行compact操作,把多個HFile合并成一個HFile。compact操作需要對HBase的資料進行多次的重新讀寫,因此這個過程會產生大量的IO。可以看到compact操作的本質就是以IO操作換取後續的讀效能的提高。
Compact的流程
HBase的compact是針對HRegion的HStore進行操作的。compact操作分為major和minor兩種,major會把HStore所有的HFile都compact為一個HFile,並同時忽略標記為delete的KeyValue(被刪除的KeyValue只有在compact過程中才真正被"刪除"),可以想象major會產生大量的IO操作,對HBase的讀寫效能產生影響。minor則只會選擇數個HFile檔案compact為一個HFile,minor的過程一般較快,而且IO相對較低。在日常任務時間,都會禁止mjaor操作,只在閒置時段定時執行。
compact入口
可以請求compact的地方有很多,包括在open region、MemStore flush等都會判斷是否需要進行compact操作(單個HStore的MemStore flush之後,如果觸發compact操作,則會對所屬HRegion下的所有HStore分別進行compact)。除此之外,HRegionServer.CompactionChecker負責定期10 * 1000s針對所有HRegion的HStore檢測是否需要進行compact操作。
CompactionChecker判斷是否需要進行compact操作的條件如下:
1、HStore下還沒有進行compact的HFile的總數 >= hbase.hstore.compaction.min(預設為3),則需要進行compact。
2、如果1不成立,則判斷是否需要執行major compact。主要是查看一下是否太久沒有執行compact操作。具體判斷過程:
1)獲得compact時間間隔。hbase.hregion.majorcompaction(預設7天)為base基準時間,hbase.hregion.majorcompaction.jitter(預設5.0)為jitter,公式base + jitter - Math.round(2 * jitter * randomNum) 計算出一個會每次自動抖動的數值作為major compact的時間間隔。之所以要一個自動抖動,就是避免在HRegionServer重啟的時候大量的major compact出現造成大量的IO。
2)所有HFile最老(時間戳記最小)的那個HFile的時間間隔大於這個major compact的時間間隔,則執行major compact。另外如果HRegion只有一個HFile,並且這個HFile的所有KeyValue的時間戳記都沒有超過TTL,則表示無須進行major compact,會跳過這次major compact。
當1或2成立都會分別對CompactSplitThread發送compact請求,不同的是,1會非同步選擇需要進行compact的HFile,2則會進行同步選擇。
compact請求
CompactSplitThread是HRegionServer內負責專門執行minor compact、major compact、split、merge操作的線程池。其內部對應4個操作有不同的線程池執行對應的請求。把這些耗時較大的操作放到各自的線程池裡有助於提高系統整個輸送量,同時可以避免某個操作阻塞影響其它操作。
對於每個compact請求,CompactionChecker需要區分出major和minor,然後分配到對應的線程池執行。條件是進行compact的檔案總大小 > hbase.regionserver.thread.compaction.throttle(預設2*maxFileCompacts*memstoreFlushSize=2*10*128MB),則為major compact,否則為minor compact。
選擇compact的檔案操作由對應的HStore進行。CompactionChecker的2會同步選擇compact檔案,這樣就可以馬上確定是哪個線程池執行具體的compact操作。但1會非同步選擇compact進行的HFile時,由於不知道檔案總大小,HBase會首先在minor compact的線程池進行compact檔案選擇操作,選擇操作後如果判斷為需要進行major compact,則會重新把請求發送到major的線程池進行後續的compact操作。
HStore的compact檔案選擇
compact檔案的選擇首先要判斷是major還是minor,如果是major,則整個HStore的所有HFile都被選中,否則就選擇部分檔案進行minor compact。考慮到compact操作都會耗費大量的IO,因此minor compact操作的目標就是以最少的IO代價換取最大的讀效能提高。目前在新版本裡,HStore的compact檔案選擇策略能夠充分考慮了整體情況去選擇最佳的方案。整個過程如下:
- 刪除無效檔案。 把超過TTL的HFile選擇為compact檔案。把這些檔案compact記錄寫入WAL,通知所有執行讀請求的scanner更新,更新HStore的總檔案大小等。
- 選擇compact檔案。
- 根據選擇compact檔案更新內部資料。
其中選擇compact檔案過程是主要步驟,具體如下:
- 把當前HStore所有的HFile作為候選compact檔案進行排除操作。
- 排除候選HFile中比正在compact的最新檔案還要老的檔案。判斷檔案新老是比較HFile裡儲存的最大SequenceId(在HLog replay的過程可以判斷哪些記錄已經寫入HFile)決定。SequenceId是HRegion把插入的KeyValue記錄寫入HLog時作為key一部分的單調遞增ID,因此SequenceId越大,則記錄越新,也就是HFile越新。
- 排除候選HFile中超過hbase.hstore.compaction.max.size(預設Long最大值)以及非Reference檔案。如果不是forceMajor則跳過這步。Reference檔案是split region產生的臨時檔案,只是簡單的引用檔案,一般必須在compact過程中刪除。
- 判斷是否major compact。滿足使用者指定的force major,或者太長時間沒有進行compact(CompactionChecker的判斷2)且候選檔案數小於hbase.hstore.compaction.max(預設10),或者有Reference檔案,滿足上面三個條件之一則是major compact。
- minor compact繼續排除操作。 1、排除在metadata裡設定不進行minor compact的HFile(bulkLoad的時候設定) 2、applyCompactionPolicy(後面詳述) 3、候選檔案數小於hbase.hstore.compaction.min(預設3)則排除全部的候選檔案
- 排除候選檔案數裡超過hbase.hstore.compaction.max(預設10)的部分,如果是major compact則跳過這步,注意從最新的HFile開始進行排除,也就是如果有12個候選檔案,則排除掉最後2個最新的HFile。
compact的選擇過程中,主要是判斷major和minor,然後在配置的最大最小相關限制下進行選擇。整個步驟的重點在applyCompactionPolicy,使用者可以實現自己的選擇策略,HBase主要有兩個策略RatioBasedCompactionPolicy和ExploringCompactionPolicy。我們首先假設一個現象:當寫請求非常多,導致不斷產生HFile,但compact的速度遠遠跟不上HFile產生的速度,這樣就會使HFile的數量會越來越多,導致讀效能急劇下降。為了避免這種情況,在HFile的數量過多的時候會限制寫請求的速度:在每次執行MemStore flush的操作前,如果HStore的HFile數超過hbase.hstore.blockingStoreFiles (預設7),則會阻塞flush操作hbase.hstore.blockingWaitTime時間,在這段時間內,如果compact操作使得HStore檔案數下降到回這個值,則停止阻塞。另外阻塞超過時間後,也會恢複執行flush操作。這樣做就可以有效地控制大量寫請求的速度,但同時這也是影響寫請求速度的主要原因之一。
兩者實現如下:
- RatioBasedCompactionPolicy。 從最舊檔案開始遍曆到最新候選檔案,找到小於[hbase.hstore.compaction.min.size(預設為memstore的flush大小,128M)和compact檔案總大小*ratio 的最大值]的符合條件檔案,如果發現不符合則馬上停止搜尋。ratio是一個可變的比例,可以通過設定高峰期的時間來改變這個比例,在高峰期時ratio為1.2,非高峰期為5,也就是非高峰期允許compact更大的檔案(非高峰期可以耗費更大IO)。 目的是儘可能找到小檔案進行minor compact。如果判斷這個compact操作後檔案數仍然過多會阻塞flush操作,則只是簡單選擇從最老的檔案起,候選檔案數減去hbase.hstore.compaction.min(預設3)個檔案。
- ExploringCompactionPolicy。 從最舊檔案開始遍曆所有的候選檔案,找出符合[compact檔案大小 小於 hbase.hstore.compaction.max.size(預設Long最大值)且所有檔案的大小都不會超過其它檔案大小*ratio]並且效率最高[compact檔案數最多或compact大小最小]。ratio是高峰期比例。注意,由於存在限制,因此可能候選檔案被排除到為0個,這時如果判斷這個compact操作後檔案數仍然過多會阻塞flush操作,則會選擇hbase.hstore.compaction.min(預設3)個檔案起,符合最大(Long最大值)最小compact大小(128MB)的總大小最小的一個子集合。
可見ExploringCompactionPolicy是基於所有候選檔案考慮,而RatioBasedCompactionPolicy則是遍曆找到就停止。ExploringCompactionPolicy是新版本的策略,舊版本的RatioBasedCompactionPolicy當時只考慮到最大的檔案往往是最老的,但對於bulk-loaded的檔案等某些情況則會破壞這個規則,RatioBasedCompactionPolicy的演算法就不是最優的壓縮策略。
完成compact檔案選擇後,HStore儲存這次compact的結果,並返回給CompactSplitThread。
compact的執行
CompactSplitThread接下來會要求HRegion進行compact請求,HRegion會增加compact的計數值表明正在執行的compact操作,這樣可以防止compact過程中,HRegion被關閉。然後HRegion調用具體HStore的compact方法執行真正的compact操作。
HStore的compact操作步驟過程,主要就是把這些HFile寫成一個HFile。主要過程為:
- 對所有檔案建立對應的scanner,Reference有特殊的scanner。scanner的層次可以參考之前的讀請求,最終得到的是一個StoreScanner對象,另外如果是major compact,則會指定在scanner的時候忽略Delete的KeyValue。
- 建立一個臨時檔案,迴圈調用scanner的next()方法,把獲得的有序的KeyValue寫入到臨時檔案中,然後把這些KeyValue最大的SequenceId寫入metadata裡。
- 把寫到臨時檔案的compact檔案移動到HStore對應的儲存目錄。
- 把compact的結果寫入WAL,RS宕機時就可以依據WAL執行刪除舊storeFile
- 用新的compact檔案更新HStore內部的資料
- 通知執行讀請求中的scanners更新讀的HFile,刪除舊檔案(實際上將其歸檔),重新計算所有HFile總大小
可以看到在整個compact操作裡,只有最後完成compact過程才會對讀請求有影響。
完成了HStore的compact操作後,HRegion就會減去之前compact的計數值。返回到CompactSplitThread流程,如果hbase.hstore.blockingStoreFiles(預設7)減去當前的HStore的HFile數。如果<0則表示HRegion將會阻塞後續的memstore flush操作,處於stuck狀態則繼續調用requestSystemCompaction,否則執行requestSplit查看是否需要split。
至此,就完成了HStore的compact操作。
總結
HBase的compact操作能夠通過犧牲當前的IO來獲得後來的讀效能提高,為了能夠減輕compact對系統帶來的影響,每次的compact操作都應儘可能選擇IO最少,且能提升讀效能最大(檔案數最多),另外對於major compact,最好應該在叢集空閑時間手動觸發。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。