MemStore 作為 HBase 的寫緩存,保存著數據的最近一次更新,響應的 BlockCache 作為 HBase 的讀緩存,保存著最近被訪問的數據塊。HBase 順序地讀取一個數據塊到內存緩存中,讀取相鄰的數據就可以在內存中讀取而不需要再次從磁盤中讀取,有效減少磁盤IO。使用 Scan API 掃描的時候,建議關閉 BlockCache,Scan 的場景中緩存意義不大。
HBase 讀取數據的時候優(yōu)先查找 MemStore(最新的更新版本),其次會查找 BlockCache。首先了解一下 HBase Block 的概念。
HBase Block
在 HBase 中,存儲文件被劃分成若干個小存儲塊,默認是64k。在 get 或 scan 中一次會完全加載一個 block 到內存中。不同于 HDFS Block,HDFS 層面的塊是用于拆分大文件以提供分布式存儲,HBase Block 塊存在于 HFile 的內部的數據結構,參考 HFile v1 版本:(詳細請看 HFile 詳解)

在建表語句中可以通過參數BlockSize指定:BLOCKSIZE => '65536'
HBase BlockCache
一個 RegionServer 有一個 BlockCache,在 RegionServer 啟動的時候完成 BlockCache 的初始化工作(參考 RegionServer 啟動源碼)。
HBase 提供了幾種 BlockCache 方案:
- LruBlockCache
- SlabCache,HBASE-4027 0.92版本提供,在1.0版本后被廢棄 HBASE-11307
- BucketCache,HBASE-7404 0.95版本提供
- ExternalBlockCache,HBASE-13170 1.10版本提供
LruBlockCache
LruBLockCache 在 JVM 堆中,基于客戶端對數據的訪問頻率,定義了三個不同的優(yōu)先級隊列,設計原理類似于 JVM 的堆分區(qū)策略。
BlockCahce 分級
- single:block 被第一次訪問,則該 Block 被放在這一優(yōu)先級隊列中。
- multi:如果一個 Block 被多次訪問,則從 single 移到 multi 中。
- in memory:in memory 由用戶指定,在內存中常駐,一般不推薦,只用系統(tǒng)表才使用 in memory 優(yōu)先級。
分級的好處在于:
首先,通過 in memory 類型緩存,將重要的數據放到 RegionServer 內存中常駐,例如 Meta 或者 namespace 的元數據信息。
其次,通過區(qū)分 single 和 multi 類型緩存,可以防止由于 scan 操作帶來的 cache 頻繁更替。
默認配置下,對于整個 BlockCache,按照以下百分比分配給 single、multi 和 in memory 使用:0.25、0.5和0.25。無論哪個區(qū),都會采用嚴格的 Least-Recently-Used 算法淘汰機制,最少使用的 Block 會被替換,為新加載的 Block 預留空間。
如果只使用 LruBlockCache,在內存較大時會存在GC的問題導致服務中斷。
SlabCache
為了解決 LRUBlockCache 方案中因為JVM垃圾回收導致的服務中斷,SlabCache 方案使用 Java NIO DirectByteBuffer 技術實現了堆外內存存儲,不再由JVM管理數據內存。
默認情況下,系統(tǒng)在初始化的時候會分配兩個緩存區(qū),分別占整個 BlockCache 大小的80%和20%,其中前者主要存儲小于等于 64K Block,后者存儲小于等于 128K Block,如果一個 Block 超過128K 則兩個區(qū)都不會緩存。SlabCache 也使用 LRU 算法對過期 Block 進行淘汰。和 LRUBlockCache 不同的是,SlabCache 淘汰 Block 的時候只需要將對應的 bufferbyte 標記為空閑,后續(xù) block cache 對其上的內存直接進行覆蓋即可。
由于以下設計上的原因被廢棄:
- 只能存儲兩種大小標準的 Block,由于不同表和列族的設置,會有多種類型的 block size,這樣會導致內存使用率低,特別是在使用了 DataBlockEncoding 的情況下。
- 因此,通常會將 SlabCache 和 LRUBlockCache 搭配使用,稱為 DoubleBlockCache。Block 會在 SlabCache 和 LruBlockCache 都緩存一份,讀操作會先查找 LruBlockCache,后查找 SlabCache,當在 SlabCache 中命中時會把 block 重新放回 LruBlockCache。實際應用中比單獨用 LruBlockCache 沒有明顯改善。
- 堆外內存的性能沒有堆內存高
BucketCache
SlabCache 方案在實際應用中并沒有很大程度改善原有 LruBlockCache 方案的GC弊端,還額外引入了諸如堆外內存使用率低的缺陷。
BucketCache 為了解決了 SlabCache 中存在的問題,首先其支持多種 Cache 方式,通過 hbase.bucketcache.ioengine 配置,有 heap、offheap 和 file 三種:
- heap 使用jvm中的heap
- offheap 使用堆外內存
- file 使用文件的方式,利用SSD硬盤的使用,改進了使用率低的問題。
其次支持了多種不同大小的 bucket,以適應不同大小的 block size??梢酝ㄟ^參數 hbase.bucketcache.bucket.sizes 來配置不同 bucket 的大小。默認是14種,大小分別是4、8、16、32、40、48、56、64、96、128、192、256、384、512KB的block(逗號分隔)。并且,在某一大小類型的 Bucket 空間不足的情況下,系統(tǒng)也會從其他 Bucket 空間借用內存使用,不會出現內存使用率低的情況。
最后,使用堆外內存的性能問題(如拷貝內存)在2.0版本中解決 HBASE-11425
實際實現中,常將 BucketCache 和 LRUBlockCache 搭配使用,稱為 CombinedBlockCache。
在 cache block 的時候會將 MetaBlock(包括 META、INDEX、BLOOM 等非DATA block)放入LruBlockCache,將 DataBlock 存儲在 BucketCache 中。特殊情況是,表中設置了 CACHE_DATA_IN_L1 => 'true' 的 DataBlock 也會存入 LruBlockCache。
ExternalBlockCache
ExternalBlockCache 提供使用外部的緩存服務來進行緩存,如 memcached 和 redis 等。
更具體的緩存細節(jié)參考 HBase BlockCache源碼
HBase 讀路徑
總結,HBase 讀路徑為,首先檢查 MemStore,然后檢查 BlockCache,最后檢索 HFile,并且合并一條數據的信息(read merge)返回給客戶端。