HBase BlockCache简介

MemStore 作为 HBase 的写缓存,保存着数据的最近一次更新,响应的 BlockCache 作为 HBase 的读缓存,保存着最近被访问的数据块。HBase 顺序地读取一个数据块到内存缓存中,读取相邻的数据就可以在内存中读取而不需要再次从磁盘中读取,有效减少磁盘IO。使用 Scan API 扫描的时候,建议关闭 BlockCache,Scan 的场景中缓存意义不大。

HBase 读取数据的时候优先查找 MemStore(最新的更新版本),其次会查找 BlockCache。首先了解一下 HBase Block 的概念。

HBase Block

在 HBase 中,存储文件被划分成若干个小存储块,默认是64k。在 get 或 scan 中一次会完全加载一个 block 到内存中。不同于 HDFS Block,HDFS 层面的块是用于拆分大文件以提供分布式存储,HBase Block 块存在于 HFile 的内部的数据结构,参考 HFile v1 版本:(详细请看 HFile 详解)


HBasel HFile v1

在建表语句中可以通过参数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 堆中,基于客户端对数据的访问频率,定义了三个不同的优先级队列,设计原理类似于 JVM 的堆分区策略。

BlockCahce 分级

  • single:block 被第一次访问,则该 Block 被放在这一优先级队列中。
  • multi:如果一个 Block 被多次访问,则从 single 移到 multi 中。
  • in memory:in memory 由用户指定,在内存中常驻,一般不推荐,只用系统表才使用 in memory 优先级。

分级的好处在于:
首先,通过 in memory 类型缓存,将重要的数据放到 RegionServer 内存中常驻,例如 Meta 或者 namespace 的元数据信息。
其次,通过区分 single 和 multi 类型缓存,可以防止由于 scan 操作带来的 cache 频繁更替。
默认配置下,对于整个 BlockCache,按照以下百分比分配给 single、multi 和 in memory 使用:0.25、0.5和0.25。无论哪个区,都会采用严格的 Least-Recently-Used 算法淘汰机制,最少使用的 Block 会被替换,为新加载的 Block 预留空间。

如果只使用 LruBlockCache,在内存较大时会存在GC的问题导致服务中断。

SlabCache

为了解决 LRUBlockCache 方案中因为JVM垃圾回收导致的服务中断,SlabCache 方案使用 Java NIO DirectByteBuffer 技术实现了堆外内存存储,不再由JVM管理数据内存。

默认情况下,系统在初始化的时候会分配两个缓存区,分别占整个 BlockCache 大小的80%和20%,其中前者主要存储小于等于 64K Block,后者存储小于等于 128K Block,如果一个 Block 超过128K 则两个区都不会缓存。SlabCache 也使用 LRU 算法对过期 Block 进行淘汰。和 LRUBlockCache 不同的是,SlabCache 淘汰 Block 的时候只需要将对应的 bufferbyte 标记为空闲,后续 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 空间不足的情况下,系统也会从其他 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 等。

更具体的缓存细节参考 HBase BlockCache源码

HBase 读路径

总结,HBase 读路径为,首先检查 MemStore,然后检查 BlockCache,最后检索 HFile,并且合并一条数据的信息(read merge)返回给客户端。


你可能感兴趣的:(HBase BlockCache简介)