一、问题分析
核心:HBase是使用Java语言开发的,所以需要依赖于JVM。
HBase的MemStore本质上是一块缓存,这就会面临Java的GC问题,在数据写入MemStore时,写入数据会产生内存碎片。并且RegionServer由多个Region组成,每一个Region根据列簇的个数多少具有相同数量的MemStore,MemStore的数据会混合在一起写入堆内存中(堆内存是共享的)。
二、过程分析
- 上图中不同颜色方块表示4个Region
- 白色方块为可分配空间
当Region3执行flush操作将数据落入磁盘,蓝色区域被释放,为后续数据的写入分配空间,分配时会生成更小的内存空间。随着MemStore中的数据不断写入和flush,JVM会产生大量的内存碎片,导致最后无法给对象分配足够的空间,这时会触发Full GC来合并内存碎片。
三、优化:MSLAB
HBase使用了一种GC优化的技术,可以有效地减少Full GC的频率——MSLAB
- 每一个MemStore实例化得到一个MemStoreLAB实例
- MemStoreLAB申请一个2M的Chunk数组,维护一个偏移量Offset=0
- 每次一个Key-Value数据插入MemStore后,MemStoreLAB将这个数据转化成数组,复制到Chunk数组中,Offset随之增长
- Chunk满了后,再申请一个新的2M的Chunk数组。
优化点:此时如果进行flush操作,产生的内存碎片的粒度变粗(因为我flush释放掉的都是2M的Chunk数组,释放内存新产生的碎片也将大于等于2M)
四、深度优化:MemStoreChunkPool
MSLAB优化后,还存在一些问题。当Chunk数组写满后,会再申请一个Chunk数组,多次的申请会在新生代触发YongGC。可以对这种情况进行优化,将Chunk数组进行复用。
- 创建一个ChunkPool来管理未被引用的Chunk数组(这样不会被垃圾回收)
- 当Chunk数组不存在引用,放回Pool中
- 当ChunkPool达到最大容量,不再接受新的Chunk数组
- 如果需要Chunk数组,先去Pool中查看是否有可用的Chunk,没有则申请新Chunk数组
- 感觉有用的话,点个赞鼓励一下~