1. 程式人生 > >HBase LRUBlockCache與BucketCache二級快取機制原理剖析與引數調優

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執行所必須的其他物件