分类: NOSQL
2013-03-01 16:35:23
Hbase中Full GC的避免
廉价商用服务器上的内存数量在过去的几年中不断的增长。当Apache的HBase项目于2007年开始时,运行Hadoop的典型配置有4到8GB内存。今天,大多数Hadoop客户以至少24G内存运行Hadoop,使用48G甚至72G内存的客户也变得越来越普遍,而内存使用成本则继续回落。表面上,这对像HBase数据库这样对延迟敏感的软件来说,这似乎是一个伟大的胜利,大量的内存可以容纳更多的数据缓存,在刷新到磁盘前作为写入缓存,避免昂贵的磁盘寻址和读。然而在实践中,HBase使用的内存不断增长,但JDK可用的垃圾收集算法仍然相同。这导致了HBase的许多用户的一个主要问题:随着Java使用堆大小继续增长,产生的内存碎片也会不断的增加,最终导致Full GC问题。
HBase中的内存碎片
Hbase的系统架构如图1,由图1可知HBase为了提高写入性能,为每个region添加了一个内存写缓存Memstore。当单个Memstore的大小达到memstore.size或Heap内存达到hbase.regionserver.global.memstore.upperLimit/lowerLimit百分比限制时,就会触发整个region的flush,最终将所有数据写入HDFS并释放region下所有Memstores占用的内存(GC不一定及时)。而Region flush最终会导致大量的内存碎片(见图2),进而触发Full GC问题。
图1 Hbase的系统架构
图2中,左边五颜六色的是不同的region在内存中的位置,它是无序的,因为客户端的请求是无规律的。此时假设黄色的region触发了flush,那么右边将会出现与之对应的多个空洞,即内存碎片。这张图以region为粒度,仅仅是为了更直观地表示这种现象。真实场景中,这些空洞是更细粒度的Key/Value级对象,它能直接导致创建对象时触发Full GC。
Arena Allocation
Arena Allocation是一种非传统的内存管理方法。它通过顺序化分配内存,内存数据分块等特性使内存碎片粗化,有效改善了内存碎片导致的Full GC问题。
它的原理有:
(1)创建一个大小固定的bytes数组和一个偏移量,默认值为0。
(2)分配对象时,将新对象的data bytes复制到数组中,数组的起始位置是偏移量,复制完成后为偏移量自增data.length的长度,这样做是防止下次复制数据时不会覆盖掉老数据(append)。
(3)当一个数组被充满时,创建一个新的数组。
(4)清理时,只需要释放掉这些数组,即可得到固定的大块连续内存。
在Arena Allocation方案中,数组的大小影响空间连续性,越大内存连续性越好,但内存平均利用率会降低。
HBase的解决方案-MSLAB
MSLAB,全称是 MemStore-Local Allocation Buffer,它基于Arena Allocation解决了HBase因Region flush导致的内存碎片问题。
MSLAB的实现原理:
(1)MemstoreLAB为Memstore提供Allocator。
(2)创建一个2M(默认)的Chunk数组和一个chunk偏移量,默认值为0。
当Memstore有新的KeyValue被插入时,通过KeyValue.getBuffer()取得data bytes数组。将data复制到Chunk数组起始位置为chunk偏移量处,并增加偏移量=偏移量+data.length。
(3)当一个chunk满了以后,再创建一个chunk。
(4)所有操作的lock free,基于CMS原语。
MSLAB的优势在于:
(1)Key/Value格式的原始数据在minor gc时被销毁。
(2)数据存放在2M大小的chunk中,chunk归属于memstore。
(3)flush时,只需要释放多个2M的chunks,chunk未满也强制释放,从而为Heap腾出了多个2M大小的内存区间,减少碎片的密集程度。由此避免由于大量内存碎片而导致的Full GC问题。