BlockCache
提升读取性能方法:热点数据存储到内存中,以避免昂贵的IO开销。
HBase也实现了一种读缓存结构——BlockCache。
客户端读取某个Block,首先会检查该Block是否存在于Block Cache,如果存在就直接加载出来,如果不存在则去HFile文件中加载,加载出来之后放到Block Cache中,后续同一请求或者邻近数据查找请求可以直接从内存中获取,以避免昂贵的IO操作。
BlockCache主要用来缓存Block。需要关注的是,Block是HBase中最小的数据读取单元,即数据从HFile中读取都是以Block为最小单元执行的。
BlockCache是RegionServer级别的,一个RegionServer只有一个BlockCache,在RegionServer启动时完成BlockCache的初始化工作。
BlockCache 三种:
LRUBlockCache
SlabCache
BucketCache
不同之处主要在于内存管理模式,其中LRUBlockCache是将所有数据都放入JVM Heap中,交给JVM进行管理。
而后两种方案采用的机制允许将部分数据存储在堆外。
这种演变本质上是因为LRUBlockCache方案中JVM垃圾回收机制经常导致程序长时间暂停,而采用堆外内存对数据进行管理可以有效缓解系统长时间GC。
LRUBlockCache
ConcurrentHashMap管理BlockKey到Block的映射关系,缓存Block只需要将BlockKey和对应的Block放入该HashMap中,查询缓存就根据BlockKey从HashMap中获取即可。
- 1.缓存分层策略
single-access、multi-access和in-memory,分别占到整个BlockCache大小的25%、50%、25%。
在一次随机读中,一个Block从HDFS中加载出来之后首先放入single-access区,后续如果有多次请求访问到这个Block,就会将这个Block移到multi-access区。而in-memory区表示数据可以常驻内存,一般用来存放访问频繁、量小的数据,比如元数据,用户可以在建表的时候设置列簇属性IN_MEMORY=true,设置之后该列簇的Block在从磁盘中加载出来之后会直接放入in-memory区。
-
LRU淘汰算法实现
在每次cache block时,系统将BlockKey和Block放入HashMap后都会检查BlockCache总量是否达到阈值,如果达到阈值,就会唤醒淘汰线程对Map中的Block进行淘汰。系统设置3个MinMaxPriorityQueue,分别对应上述3个分层,每个队列中的元素按照最近最少被使用的规则排列,系统会优先取出最近最少使用的Block,将其对应的内存释放。可见,3个分层中的Block会分别执行LRU淘汰算法进行淘汰。 -
LRUBlockCache方案优缺点
随着数据从single-access区晋升到multi-access区或长时间停留在single-access区,对应的内存对象会从young区晋升到old区,晋升到old区的Block被淘汰后会变为内存垃圾,最终由CMS回收(Conccurent Mark Sweep,一种标记清除算法),显然这种算法会带来大量的内存碎片,碎片空间一直累计就会产生臭名昭著的Full GC。尤其在大内存条件下,一次Full GC很可能会持续较长时间,甚至达到分钟级别。Full GC会将整个进程暂停,称为stop-the-world暂停(STW),因此长时间Full GC必然会极大影响业务的正常读写请求。
SlabCache
SlabCache方案提出使用Java NIO DirectByteBuffer技术实现堆外内存存储,不再由JVM管理数据内存。
a.Java NIO DirectByteBuffer技术实现堆外内存存储,不再由JVM管理数据内存
b. BlockCache大小的80%和20%,每个缓存区分别存储固定大小的Block,其中前者主要存储小于等于64K的Block,后者存储小于等于128K的Block,如果一个Block太大就会导致两个区都无法缓存。
c. DoubleBlockCache方案有很多弊端。比如,SlabCache中固定大小内存设置会导致实际内存使用率比较低,而且使用LRUBlockCache缓存Block依然会因为JVM GC产生大量内存碎片。
BucketCache
BucketCache工作模式
heap: 数据存在JVM,会发生GC,但是读取方便
offheap: 因为内存属于操作系统,所以大大降低了因为内存碎片导致Full GC的风险。但是读取数据是,需要从offheap复制到JVM heap,读取耗时。
file: 一般使用ssd,加个昂贵,但是因存储空间大,极大提高了缓存命中率。