一.简单介绍
1.在以前学习Hbase的时候,对BlockCache几乎是没有听说过,不过它也是比较重要的hbase的一部分,有些在生产环境中的性能问题,都是靠优化BlockCache来解决的。
2.首先我们要知道一个RegionServer只有一个BlockCache。我们根据其单词名称可以判断是以恶搞块缓存,而往往缓存都是和内存有关,有了这些,接下来就好懂了。
3.BlockCache知识用于优化读取性能的,可以设置不开启,默认是开启的。
可以使用下面的shell命令进行关闭
alter 'table1',CONFIGURATION=>{NAME=>'cf',BLOCKCACHE=>'false'}
接下来介绍几种BlockCache的实现方案
二.BlockCache的实现方案
2.1.LRUBlock Cache
LRU的意思是Leaset Recently Used 即最近最少使用,我们在学习操作系统学过的LRU,FIFO一样。读出的block会放到BlockCache 中待下次查询使用。如果缓存满了会根据LRU淘汰算法进行淘汰相关的block。
LRUBlockCache分为三个部分:
这个方案是仿照Java的JVM而设计的
in-memory:这个属性并不是说这个列族就会存放到列族中,而是和其他的列族一样会存放到硬盘上。一般的列族经过第一次被访问会存放到singke-access中,多次访问会存放到multi-access中。
对于in-memory=true的列族,会被存放到in-memeory,并且在in-memory区域里的列族的block拥有最长存活时间,当需要删除block的,这个区域的列族是最后考虑的
仅仅只是为了BlockCache而创造的。
和MemStore的联动关系
当我们设置hfile.block.cache.size的时候需要主要memStore的影响
MemStore+BlockCache的总内存不能够超过80%,巧合的是,二者默认的最大内值都是0.4,所以如果你需要改动其中一个的话,另外一个也需要改动。一般情况下不需要改动
LRUBlock Cache的坏处?
随着于内存对象的增多,如果引发Full GC 那么就可怕了,将会导致整个JVM成语停止状态,有时候会有几分钟的停滞,这也是Spark core的劣势之一,感兴趣的话,可以了解一下Spark。
2.2.SlabCache
2.2.1.SlabCache的说明
对于LRUBlockCache引发GC,那么我们就使用这个方案就可以解决,怎样解决呢?还是使用堆内存吗?当然不可能了,既然不适用堆内存,那么只能使用堆外内存来拜托GC的限制了。
可以使用-XX:MaxDirectMe morySize=10MB进行设置
尽管可以使JVM不会被停顿,但是它带来的坏处更多:
(1).堆外内存就相当于一片无人管理的荒野,存储都是原始的数据,如果是个对象必须序列化后才能存储,因此不能存储复杂的对象
(2).和JVM没啥关系,所有内存泄漏,排查起来比较困难
(3).使用系统内存,如果使用量过于大的话,有可能物理内存会爆掉,也有可能会影响硬盘使用
2.2.2.SlabCache的实现
SlabCache被划分为两个区域,区域大小是按照堆外内存的80%,20%进行划分,并且调用了nio的DirectByteBuffer进行实现的
两个区域分别
(1)存放大小约等于1个BlockSize(64KB)默认值的Block。
(2)存放大小约等于2个BlockSize默认值的Block。
如果你存放的Block的大小大于BlockSize的1倍到2倍之间,那么将不会被存储,那么将会使用LRU对这些没有存储的对象进行淘汰
BlockSize的值可以通过修改列族的BLOCKSIZE,如果你修改了BLOCKSIZE的属性,这两个区域都不会被用到,SlabCache将会和LRU一起使用
当一个Block被取出的时候同时被放到SlabCache和LRUCache中。
当读请求到来的时候先查看LRUCache,如果查不到就去查
SlabCache,如果查到了就把Block放到LRUCache中。
感觉SlabCache 是二级缓存,并且在生产环境中,这个方案对GC的改善并不是很大,用得不是很多
2.3. Bucket Cache
2.3.1.介绍
和SlabCache一样,Bucket Cache也是使用堆外内存。
和SlabCache类似,不过也改进了许多
-
区域并不是两个了,而是初始化的是十四个区域Block大小分别为4KB、8KB、16KB、32KB、40KB、48KB、56KB、64KB、96KB、128KB、192KB、256KB 384KB、512KB,并且区域数量以及Block大小都是可以改变的
可以通过hbase.bucketcache.bucket.sizes进行配置,配置多个使用逗号进行分割 -
BucketCache并不一定会采用堆外内存的方式,而是有三种选择,堆(heap) 堆外(offheap) 以及文件(file) 可以通过hbase.bucketcache.ioengine设置,默认是offheap
-
每个Block的大小上限是最大尺寸的block*4
比如可以容纳的最大的Block类型是512KB,那么每个Bucket的大小就是512KB *
4 = 2048KB -
系统一启动BucketCache就会把可用的存储空间按照每个Bucket
的大小上限均分为多个Bucket。如果划分完的数量比你的种类还
少,比如比14(默认的种类数量)少,就会直接报错,因为每一
种类型的Bucket至少要有一个Bucket
2.3.2.好处:
-
BucketCache还有一个特别的长处,那就是它自己来划分内存空
间、自己来管理内存空间,Block放进去的时候是考虑到offset偏移量
的(具体可以看源码的BucketAllocator),所以内存碎片少,发生GC
的时间很短。 -
之前说的file,其实就是SSD,这个也是这个策略的亮点之一
-
这种策略极大地改进了SlabCache使用率低的问题。
-
配置极其灵活,可以适用于多种场景。
在实际生产环境中,性能也是很不错
如果你想要某个列族不使用此策略
可以使用命令:意思是只缓存在LRU中
base(main):004:0> alter 'user',CONFIGURATION=>{CACHE_DATA_IN_L1=>'true'}
Updating all regions with the new schema...
1/1 regions updated.
Done.
2.3.2.配置参数:
-
hbase.bucketcache.combinedcache.enabled:是否打开组合模
式(CombinedBlockCache),默认为true -
hbase.bucketcache.ioengine:使用的存储介质,可选值为
heap、offheap、file。不设置的话,默认为offheap -
hbase.bucketcache.size:BucketCache所占的大小
如果设置为0.0~1.0,则代表了占堆内存的百分比。
如果是大于1的值,则代表实际的BucketCache的大小,单位为MB。
默认值为0.0,即关闭BucketCache
这个是因为BucketCache既可以用于堆内存,还可以用于堆外内存和硬盘,但是并不推荐一参二用 -
hbase.bucketcache.bucket.sizes:定义所有Block种类,默认为14种,种类之间用逗号分隔。单位为B,每一种类型必须是1024的整数倍,否则会报异常java.io.IOException: Invalid HFile block magic。
默认值为:4、8、16、32、40、48、56、
64、96、128、192、256、384、512。 -
-XX:MaxDirectMemorySize:这个参数不是在hbase-site.xml中
配置的,而是JVM启动的参数。如果你不配置这个参数,JVM会按
需索取堆外内存;如果你配置了这个参数,你可以定义JVM可以
获得的堆外内存上限。显而易见的,这个参数值必须比
hbase.bucketcache.size大。
3.组合模式
之前说了两种方案,但是请记住,无论是SlabCache还是比SaltCache更好一些的BucketCache,都是无法和默认的LRUCache媲美的,尽管它存在GC的问题
用SlabCache时候,一般情况下,是要个LRU组合使用的,LRU为一级缓存,SlabCache为二级缓存
用BucketCache的时候也是一样,不会只会单独使用它,但是和SlabCache不一样,而是使用组合模式(CombinedBlockCahce)
就是说会根据Block的类型来决定存放的位置。比如Index Block和Bloom Block会被放到LRUCache中。Data Block被直接放到BucketCache中,
所以数据会去LRUCache查询一下,然后再去BucketCache中查询真正的数据。
其实这种实现是一种更合理的二级缓存,数据一级缓存到二级缓存最后到硬盘,数据是从小到大,存储介质也是由快到慢。考虑到成本和性能的组合,比较合理的介质是:LRUCache使用内存->BuckectCache使用SSD->HFile使用机械硬盘。
LRUCache和BucketCache的比较:
- 为BucketCache自己控制内存空间,碎片比较少,所以GC时间大部分时间都比LRUCache短
- 在缓存全部命中的情况下,LRUCache的吞吐量是BucketCache的两倍;在缓存基本命中的情况下,LRUCache的吞吐量跟BucketCache基本相等。
- 读写延迟,IO方面两者基本相等。
- 缓存全部命中的情况下,LRUCache比使用fiile模式的BucketCache CPU占用率低一倍,但是跟其他情况下差不多
因此从整体情况来看,默认的LRUCache合适要好于Buckcache的,只不过如果存在Full GC时候也可以使用BucketCache适当调节一下