您现在的位置是:首页 > 文章详情

HBase blockcache原理介绍

日期:2020-04-01点击:554

1.核心组件

blockcache_

总入口是CacheConfig,这个类根据配置信息,来返回不同的具体cache组件;

默认会返回LruBlockCache,所有类型的block都会存入;

启用了BucketCache时,会返回CombinedBlockCache,此类中根据block类型,data block存入BucketCache,其它存入LruBlockCache;

2.LruBlockCache

blockcache_LRU
LruBlockCache内部较为简单,主要就是一个map,如上图所示,由hfilename+offset来唯一标识一个block;

LruBlockCache所能够使用的内存为堆的一定比例,通过hfile.block.cache.size设置,默认是0.4;
so,maxSize = heapSize * hfile.block.cache.size,以下参数都根据maxSize计算;

blockcache_LRU_maxsize
acceptSize:
使用量达到一定比例时会触发驱逐,该阈值通过hbase.lru.blockcache.acceptable.factor设置,默认是0.99;

minSize:
驱逐后最少剩余比例,该阈值通过hbase.lru.blockcache.min.factor设置,默认是0.95;

hardLimit:
使用量达到一定比例时则拒绝写入,该阈值通过hbase.lru.blockcache.hard.capacity.limit.factor设置,默认是1.2,这意味允许一定的超出;

关于驱逐:

  1. block分为3种类型,由BlockPriority字段区分,取值为single、mutli、inMem,空间分配默认为0.25:0.5:0.25;
  2. 系统表以及其它指定了InMem的表所含block会标记为inMem,其它block初次存入时标记为single,再次访问时会修改为multi;
  3. 存放时只要还有空间即可放入,空间分配比例只是在驱逐发生时进行计算使用;
  4. 驱逐时,会用minSize乘以各类型的比例,得到各类型最少要保留的minSize;
  5. 根据目前的算法,驱逐后的size,应该是略大于minSize的一个值,伪代码如下;
expectFreeSize = usedSize - minSize;//预期释放总大小 freedSize = 0;//当前已释放总大小 n=3;//类型数量 for type in ('single','multi','inMem'): overFlow = type.usedSize - type.minSize toBeFree = min(overFlow,(expectFreeSize - freedSize)/n) free(toBeFree) freedSize += toBeFree n--;

3.BucketCache

LruBlockCache的优点是实现简单,缺点是block的存入和释放伴随着内存的申请和释放,会带来内存碎片和gc过多的问题;

BucketCache采用了类似池的思路,预先申请内存并划分为一个个的bucket,这些bucket会一直存在并重复使用;

总体的读写流程如下图所示:
blcokcache_Bucket_

Block缓存写入流程:

  1. 将block写入RAMCache,然后系统会根据blockkey进行hash,根据hash结果将block分配到一组blockingQueue中;
  2. HBase会同时启动多个WriteThead,分别关联一个blockingQueue,并发的执行异步写入;
  3. 每个WriteThead读取到block数据后,调用bucketAllocator为这些block分配内存空间;
  4. BucketAllocator会选择与block大小对应的bucket进行存放,并且返回对应的物理地址偏移量offset;
  5. WriteThead将block以及分配好的物理地址偏移量传给IOEngine模块,执行具体的内存写入操作;
  6. 写入成功后,将类似这样的映射关系写入BackingMap中,方便后续查找时根据blockkey可以直接定位;

Block缓存读取流程:

  1. 首先从RAMCache中查找,对于还没有来得及写入到bucket的缓存block,一定存储在RAMCache中;
  2. 如果在RAMCache中没有找到,再在BackingMap中根据blockKey找到对应entry;
  3. 根据entry中的offset可以直接从内存中查找对应的block数据;

其中最核心的组件是BucketAllocator和IoEngine,前者负责block的逻辑地址分配,后者负责block的实际物理存放,内部结构如下:

blockcache_Bucket_
hbase中blocksize是可以灵活设置的,bucketCache预设了一组支持的大小,从4K~512k不等;

一个Bucket只能存放一种size的block,一种size对应一个BucketSizeInfo进行管理;

初始化时,每种size先分配1个bucket,剩余的都分配给最大的那个size,如黑色箭头所示;

分配过程中当前size如果空间不够,会挪用其它size的空闲bucket,如棕色箭头所示,这意味着有可能某个Bucket一开始存放了32k的block

,后面释放后空闲,被挪用后变成存放64k的block;

ioEngine有多种实现,可支持onheap、offheap、disk等;

关于驱逐:

  1. 2种情况下会触发,1是已使用超过95%(acceptableFactor),2是某个size的block分配不了(总量虽然没达到阈值,但不存在完全空闲的bucket供挪用);
  2. 驱逐后的最少剩余比例为85%(minFactor),遍历各个bucketSizeInfo,把超过85%的部分加起来,再乘以一个系数0.1(extraFreeFactor),就是要释放的大小;
  3. 具体计算方法复用了LruBlockCache的代码,也是按照single、multi、inMem及其比例进行计算和释放;
  4. 实际清理动作是修改一些状态数据,比如Bucket对象的freeList、freeCount,以及backMapping的键值对等,并不需要对底层的byteBuffer做什么操作;
  5. 对于refCount大于0的block,会先将其markedForEvict置为true,待各个使用方读取完成后调用returnBlock进行释放;

参考资料

http://hbasefly.com/2016/04/26/hbase-blockcache-2/?xuxezc=17idz1

原文链接:https://yq.aliyun.com/articles/752980
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章