Hbase系列Blog

HBase读写数据流程(一)
Hbase中两种缓存机制memstore和blockcache详解(二)
Phoenix安装与使用和Hbase整合Phoenix(三)
如何查看HBase的HFile(四)
Hbase snapshot 快照(五)
HBase BulkLoad批量写入数据实战(六)
Hbase重启后, RegionServer自动挂机(七)


HBase在实现中提供了两种缓存结构:MemStore和BlockCache。MemStore 作为 HBase 的写缓存,保存着数据的最近一次更新,响应的 BlockCache 作为 HBase 的读缓存,保存着最近被访问的数据块。

HBase中Block的概念

1、Block是HBase中最小的数据存储单元,默认为64K,在建表语句中可以通过参数BlockSize指定。
2、HBase中Block分为四种类型:Data Block,Index Block,Bloom Block和Meta Block。
3、其中Data Block用于存储实际数据,通常情况下每个Data Block可以存放多条KeyValue数据对;
4、Index Block和Bloom Block都用于优化随机读的查找路径,
5、其中Index Block通过存储索引数据加快数据查找,
6、而Bloom Block通过一定算法可以过滤掉部分一定不存在待查KeyValue的数据文件,减少不必要的IO操作;
7、Meta Block主要存储整个HFile的元数据。

MemStore

1、其中MemStore称为写缓存
2、HBase执行写操作首先会将数据写入MemStore,并顺序写入HLog,
//代码中这样,我们的理解为 先顺序写入HLog 再将数据写入MemStore
3、等满足一定条件后统一将MemStore中数据刷新到磁盘,这种设计可以极大地提升HBase的写性能。
4、MemStore对于读性能也至关重要,假如没有MemStore,读取刚写入的数据就需要从文件中通过IO查找,这种代价显然是昂贵的!

BlockCache

一个 RegionServer 有一个 BlockCache,在 RegionServer 启动的时候完成 BlockCache 的初始化工作。

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)返回给客户端。


大家好,我是徐小慧
📢 博客主页:徐小慧_Blog
📢 欢迎 点赞 👍 | 收藏 ⭐ | 留言 📝 如有错误敬请指正!

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐