HBase LRUBlockCache與BucketCache二級快取機制原理剖析與引數調優
本文章來自轉載,轉載地址:https://juejin.im/post/5bfd666a6fb9a049ea38a55a 在此需要著重感謝華為的架構師秦凱新大佬
1 BlockCache 唯一性
- 一個 RegionServer只有一個BlockCache。
- BlockCache的誕生就是用來優化讀取效能的。
- HBase Block 目前主要有DATA,ENCODED_DATA,META,FILE_INFO,ROOT_INDEX等。
- BlockCache 目前主要有LRUBlockCache和SlabCache,以及BucketCache等。
2 LRUBlockCache的三段快取架構
-
hfile.block.cache.size.LRUBlockCache: 引數表示佔用堆記憶體比例,預設是0.4,目前這是唯一的LRUBlockCache,無法被關閉。
-
BlockCache配置和Memstore配置的聯動影響,即:Memstore+BlockCache的記憶體佔比不能超過0.8(即80%),否則就會報錯。注意必須留20%的機動空間:
hbase.regionserver.global.memstore.size+hfile.block.cache.size<=0.8 複製程式碼
-
LRUBlockCache 完全基於JVM heap的LRU的方案,當快取寫滿了之後,會根據LRU的演算法來淘汰block。
-
LRUBlockCache的三段快取架構:
- 第一段single-access佔用25%的比例,也即單次讀取區,block被讀出後先放到這個區域,當被讀到多次後會升級到下一個區域。
- 第二段multi-acsess佔用50%的比例,也即多次讀取區,當一個被緩衝到單次讀取區後又被訪問多次,會升級到這個區。
- in-memory佔用25%的比例,這個區域跟Block被訪問幾次沒有什麼關係,它只存放那些被設定了IN-MEMORY=true的列族中讀取出來的block。
3 LRUBlockCache的弊端
LRUBlockCache完全基於JVM Heap的快取,那麼勢必會造成一個後果,隨著記憶體中物件越來越多,每隔一段時間肯定會產生Full GC。
4 SlabCache 堆外記憶體的創意(已廢棄)
4.1 堆外記憶體燙手山芋
- 因為堆外記憶體儲存的資料都是原始資料,對於一個物件,比如先序列化之後才能儲存,所以不能儲存大物件。
- 堆外記憶體並不是JVM的管理範圍,所以當記憶體洩露的時候非常不好排查問題。
- 對外記憶體使用的是實體記憶體,當使用過大的時候,實體記憶體可能會爆掉。
5 BucketCache 應運而生
5.1 BucketCache理論基礎
-
CombinedBlockCache是一個LRUBlockCache和BucketCache的混合體。BucketCache是阿里貢獻的。LRUBlockCache中主要儲存Index Block和Bloom Block,而將Data Block儲存在BucketCache中。因此一次隨機讀需要首先在LRUBlockCache中查到對應的Index Block,然後再到BucketCache查詢對應資料塊。
-
BucketCache可以有三種工作模式:heap、offheap、file。heap模式表示這些Bucket是從JVM Heap中申請,offheap模式使用DirectByteBuffer技術實現堆外記憶體儲存管理,而file模式使用類似SSD的快取記憶體檔案儲存資料塊。
-
無論在哪一種工作模式下,BucketCache都會申請許多帶有固定大小標籤的Bucket,一種Bucket只是一種指定的BlockSize的資料塊,初始化的時候申請14個不同大小的Bucket,而且即使在某一種Bucket空間不足的情況下,系統也會從其他Bucket空間借用記憶體使用,不會出現記憶體使用率低下的情況。這裡每個Bucket的大小上限為最大尺寸的Block * 4,比如最大容納Block型別為512KB,那麼每個Bucket的大小就是512KB*4 =2018。若配置了4,則會有2048/4=512個4K的空間。
-
我們將物理空間劃分為一堆等大的Bucket,每一個Bucket有一個序號及一個size標籤,於是Block所在bucket的序號及其在bucket中的offset與block在物理空間的offset就形成了一一對應。我們通過BucketAllocator為指定大小的Block尋找一個Bucket進行存放,於是就得到了其在物理空間上的位置。
-
每個Bucket都有一個size標籤,目前對於size的分類,是在啟動時候就確定了,如預設的有(8+1)K、(16+1)K、(32+1)K、(40+1)K、(48+1)K、(56+1)K、(64+1)K、(96+1)K ... (512+1)K
-
相同size標籤的Bucket由同一個BucketSizeInfo管理
-
Bucket的size標籤可以動態調整,比如64K的block數目比較多,65K的bucket被用完了以後,其他size標籤的完全空閒的bucket可以轉換成為65K的bucket,但是至少保留一個該size的bucket
-
如果最大size的bucket為513K,那麼超過這個大小的block無法儲存,直接拒絕
-
如果某個size的bucket用完了,那麼會依照LRU演算法觸發block淘汰
-
hbase 表引數一覽
hbase(main):002:0> create 'Test',{NAME=>'d',IN_MEMORY=>'true'} 0 row(s) in 4.4970 seconds => Hbase::Table - Test hbase(main):003:0> describe 'Test' Table Test is ENABLED Test COLUMN FAMILIES DESCRIPTION {NAME => 'd', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'true', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'} 1 row(s) in 0.2530 seconds 複製程式碼
5.2 BucketCache的引數設定
-
BucketCache預設是開啟的,如果不想讓某個列族使用BucketCache,可以使用一下命令
alter 'mytable' , CONFIGURATION => {CACHE_DATA_IN_L1 => 'true'} 複製程式碼
-
BucketCache 相關的配置項如下:
-
hbase.bucketcache.ioengine :使用的儲存介質,可選值為heap ,offheap,file。不設定的話,預設為offheap。
-
hbase.bucketcache.combinedcache.enabled:是否開啟組合模式(combinedcache),預設是true
-
hbase.bucketcache.size:BucketCache所佔的大小
如果設定為0.0-1.0 ,則代表佔堆記憶體的百分比 如果大於1,則代表實際的BucketCache的大小,單位為MB。 預設值為0.0,即關閉BucketCache 複製程式碼
-
hbase.bucketcache.bucket.sizes:定義所有Block種類,預設是14中,預設值為4,8,16,32,40......
-
-XX:MaxDirectMemorySize:這是JVM啟動引數,該引數定義了JVM可以獲取的堆外記憶體上限。
-
5.3 BucketCache組合模式的強強合作
- 具體解釋為把不同型別的Block分別放到LRUCache和BucketCache中,如:Index Block和Bloom Block會被放到LRUCache中,Data Block 被直接放進BucketCache中,所以每次查詢,都會先去LRUCache查詢一下,然後再去BucketCache中查詢真正的資料。
- LRUCache使用記憶體,BucketCache使用SSD,HFile使用機械硬碟。
5.4 BucketCache 測試報告
- BucketCache自己使用記憶體,碎片比較少,所以GC時間大部分都要比LRUBlockCache短。
- 在快取全部命中的情況下,LRUBlockCache是BucketCache吞吐量的兩倍。在快取基本命中的情況下,LRUBlockCache是BucketCache吞吐量相當。
- 讀寫延遲,IO方面基本相當。
- 強烈建議線上配置BucketCache模式。可能很多專家都測試過這兩種模式下的GC、吞吐量、讀寫延遲等指標,看到測試結果都會很疑惑,BucketCache模式下的各項效能指標都比LruBlockCache差了好多,但是突然我弄明白了,測試肯定是在基本全記憶體場景下進行的,這種情況下確實會是如此。但是話又說回來,在大資料場景下又有多少業務會是全記憶體操作呢?
5.5 RegionServer記憶體分配
RegionServer程序的記憶體就是JVM記憶體,主要分為三部分:LRUBlockCache,用於讀快取;MemStore,用於寫快取;Other,用於RS執行所必須的其他物件