天天看點

HBase – Memstore Flush深度解析

Memstore是HBase架構中非常重要的組成部分之一,是HBase能夠實作高性能随機讀寫至關重要的一環。深入了解Memstore的工作原理、運作機制以及相關配置,對hbase叢集管理、性能調優都有着非常重要的幫助。

Memstore 概述

HBase中,Region是叢集節點上最小的資料服務單元,使用者資料表由一個或多個Region組成。在Region中每個ColumnFamily的資料組成一個Store。每個Store由一個Memstore和多個HFile組成,如下圖所示:

HBase – Memstore Flush深度解析

之前我們提到,HBase是基于LSM-Tree模型的,所有的資料更新插入操作都首先寫入Memstore中(同時會順序寫到日志HLog中),達到指定大小之後再将這些修改操作批量寫入磁盤,生成一個新的HFile檔案,這種設計可以極大地提升HBase的寫入性能;另外,HBase為了友善按照RowKey進行檢索,要求HFile中資料都按照RowKey進行排序,Memstore資料在flush為HFile之前會進行一次排序,将資料有序化;還有,根據局部性原理,新寫入的資料會更大機率被讀取,是以HBase在讀取資料的時候首先檢查請求的資料是否在Memstore,寫緩存未命中的話再到讀緩存中查找,讀緩存還未命中才會到HFile檔案中查找,最終傳回merged的一個結果給使用者。

可見,Memstore無論是對HBase的寫入性能還是讀取性能都至關重要。其中flush操作又是Memstore最核心的操作,接下來重點針對Memstore的flush操作進行深入地解析:首先分析HBase在哪些場景下會觸發flush,然後結合源代碼分析整個flush的操作流程,最後再重點整理總結和flush相關的配置參數,這些參數對于性能調優、問題定位都非常重要。

Memstore Flush觸發條件

HBase會在如下幾種情況下觸發flush操作,需要注意的是MemStore的最小flush單元是HRegion而不是單個MemStore。可想而知,如果一個HRegion中Memstore過多,每次flush的開銷必然會很大,是以我們也建議在進行表設計的時候盡量減少ColumnFamily的個數。

Memstore級别限制:當Region中任意一個MemStore的大小達到了上限(hbase.hregion.memstore.flush.size,預設128MB),會觸發Memstore重新整理。

Region級别限制:當Region中所有Memstore的大小總和達到了上限(hbase.hregion.memstore.block.multiplier * hbase.hregion.memstore.flush.size,預設 2* 128M = 256M),會觸發memstore重新整理。

Region Server級别限制:當一個Region Server中所有Memstore的大小總和達到了上限(hbase.regionserver.global.memstore.upperLimit * hbase_heapsize,預設 40%的JVM記憶體使用量),會觸發部分Memstore重新整理。Flush順序是按照Memstore由大到小執行,先Flush Memstore最大的Region,再執行次大的,直至總體Memstore記憶體使用量低于門檻值(hbase.regionserver.global.memstore.lowerLimit * hbase_heapsize,預設 38%的JVM記憶體使用量)。

當一個Region Server中HLog數量達到上限(可通過參數hbase.regionserver.maxlogs配置)時,系統會選取最早的一個 HLog對應的一個或多個Region進行flush

HBase定期重新整理Memstore:預設周期為1小時,確定Memstore不會長時間沒有持久化。為避免所有的MemStore在同一時間都進行flush導緻的問題,定期的flush操作有20000左右的随機延時。

手動執行flush:使用者可以通過shell指令 flush ‘tablename’或者flush ‘region name’分别對一個表或者一個Region進行flush。

Memstore Flush流程

為了減少flush過程對讀寫的影響,HBase采用了類似于兩階段送出的方式,将整個flush過程分為三個階段:

prepare階段:周遊目前Region中的所有Memstore,将Memstore中目前資料集kvset做一個快照snapshot,然後再建立一個新的kvset。後期的所有寫入操作都會寫入新的kvset中,而整個flush階段讀操作會首先分别周遊kvset和snapshot,如果查找不到再會到HFile中查找。prepare階段需要加一把updateLock對寫請求阻塞,結束之後會釋放該鎖。因為此階段沒有任何費時操作,是以持鎖時間很短。

flush階段:周遊所有Memstore,将prepare階段生成的snapshot持久化為臨時檔案,臨時檔案會統一放到目錄.tmp下。這個過程因為涉及到磁盤IO操作,是以相對比較耗時。

commit階段:周遊所有的Memstore,将flush階段生成的臨時檔案移到指定的ColumnFamily目錄下,針對HFile生成對應的storefile和Reader,把storefile添加到HStore的storefiles清單中,最後再清空prepare階段生成的snapshot。

上述flush流程可以通過日志資訊檢視:

整個flush過程可能涉及到compact操作和split操作,因為過于複雜,在此暫時略過不表。

Memstore Flush對業務讀寫的影響

上文介紹了HBase在什麼場景下會觸發flush操作以及flush操作的基本流程,想必對于HBase使用者來說,最關心的是flush行為會對讀寫請求造成哪些影響以及如何避免。因為不同觸發方式下的flush操作對使用者請求影響不盡相同,是以下面會根據flush的不同觸發方式分别進行總結,并且會根據影響大小進行歸類:

影響甚微

正常情況下,大部分Memstore Flush操作都不會對業務讀寫産生太大影響,比如這幾種場景:HBase定期重新整理Memstore、手動執行flush操作、觸發Memstore級别限制、觸發HLog數量限制以及觸發Region級别限制等,這幾種場景隻會阻塞對應Region上的寫請求,阻塞時間很短,毫秒級别。

影響較大

然而一旦觸發Region Server級别限制導緻flush,就會對使用者請求産生較大的影響。會阻塞所有落在該Region Server上的更新操作,阻塞時間很長,甚至可以達到分鐘級别。一般情況下Region Server級别限制很難觸發,但在一些極端情況下也不排除有觸發的可能,下面分析一種可能觸發這種flush操作的場景:

相關JVM配置以及HBase配置:

基于上述配置,可以得到觸發Region Server級别的總Memstore記憶體和為24.9G,如下所示:

假設每個Memstore大小為預設128M,在上述配置下如果每個Region有兩個Memstore,整個Region Server上運作了100個region,根據計算可得總消耗記憶體 = 128M * 100 * 2 = 25.6G > 24.9G,很顯然,這種情況下就會觸發Region Server級别限制,對使用者影響相當大。

根據上面的分析,導緻觸發Region Server級别限制的因素主要有一個Region Server上運作的Region總數,一個是Region上的Store數(即表的ColumnFamily數)。對于前者,根據讀寫請求量一般建議線上一個Region Server上運作的Region保持在50~80個左右,太小的話會浪費資源,太大的話有可能觸發其他異常;對于後者,建議ColumnFamily越少越好,如果從邏輯上确實需要多個ColumnFamily,最好控制在3個以内。

總結

本文主要介紹了HBase引擎中至關重要的一個元件-Memstore,主要介紹了Memstore Flush的幾種觸發條件、Flush完整流程以及各種不同場景下Flush對業務讀寫的影響。希望通過此篇文章可以對Memstore有一個更深入的了解。

本文轉載自:http://hbasefly.com

<a href="http://hbasefly.com/2016/03/23/hbase-memstore-flush/" target="_blank">原文連結</a>