随着内存资源价格的降低,服务器的内存越来越大,很多都是达到96GB的。而HBase的RS又是内存耗用性的,很多时候我们为其分配了比较大的内存空间。但与此同时,很多人都会遇到配置大内存所导致的各种问题。
首先,我们知道HBase工作依赖于Zookeeper,RS会定期向Master进行状态汇报,如果长时间没有收到RS的汇报信息,Master会认为RS已经死掉,然后开始进行恢复操作。而Zookeeper与RS的会话也可能因为STW的GC导致会话超时,自己便会退出。
其次,很大堆内存一旦触发full gc,除了可能导致长时间的停顿,客户端感觉到明显的延迟甚至timeout,另外一个比较严重的是随着时间推移,堆内存中存在大量内存碎片,带来的结果是虽然还有很多空余内存,但很多是比较小无法分配使用的,promotion failure。
生产环境一般新生代使用ParNew, 老年代用CMS,触发尽量别配置太大比例,否则容易产生晋升失败。
HBase提供了以下几个参数便于利用上一种机制,防止堆中产生过多碎片,大概思路也是参考TLAB,叫做MSLAB,MemStore-Local Allocation Buffer,为memstore划分相应的区块MemStoreLAB,里面一个字节数组固定的大小(curChunk),用于存放KeyValue的具体数值,还有一个变量用于记录目前空余空间偏移指针,当一个Chunk被填满,重新开辟一个字节数组空间。有效减少了因为内存碎片导致的full gc。
MSLAB,全称是 MemStore-Local Allocation Buffer,是Cloudera在HBase 0.90.1时提交的一个patch里包含的特性。它基于Arena Allocation解决了HBase因Region flush导致的内存碎片问题。
MSLAB的实现原理(对照Arena Allocation,HBase实现细节):
MemstoreLAB为Memstore提供Allocator。
创建一个2M(默认)的Chunk数组和一个chunk偏移量,默认值为0。
当Memstore有新的KeyValue被插入时,通过KeyValue.getBuffer()取得data bytes数组。将data复制到Chunk数组起始位置为chunk偏移量处,并增加偏移量=偏移量+data.length。
当一个chunk满了以后,再创建一个chunk。
所有操作lock free,基于CMS原语。
优势:
KeyValue原始数据在minor gc时被销毁。
数据存放在2m大小的chunk中,chunk归属于memstore。
flush时,只需要释放多个2m的chunks,chunk未满也强制释放,从而为Heap腾出了多个2M大小的内存区间,减少碎片密集程度。
开启MSLAB
hbase.hregion.memstore.mslab.enabled=true // 开启MSALB
hbase.hregion.memstore.mslab.chunksize=2m // chunk的大小,越大内存连续性越好,但内存平均利用率会降低
hbase.hregion.memstore.mslab.max.allocation=256K // 通过MSLAB分配的对象不能超过256K,否则直接在Heap上分配,256K够大了
参考:
http://blog.sina.com.cn/s/blog_6b10e1740100rxnk.html
http://blog.cloudera.com/blog/2011/02/avoiding-full-gcs-in-hbase-with-memstore-local-allocation-buffers-part-1/
http://blog.cloudera.com/blog/2011/02/avoiding-full-gcs-in-hbase-with-memstore-local-allocation-buffers-part-2/
http://blog.cloudera.com/blog/2011/03/avoiding-full-gcs-in-hbase-with-memstore-local-allocation-buffers-part-3/