1. 程式人生 > >HBase的compact分析

HBase的compact分析

next() 生成 最大值 log tex next thread block tail

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,最好應該在集群空暇時間手動觸發。

HBase的compact分析