最近一直在研究Hbase,上班时间能去研究这些hbase一些稍微深层次的原理,或者做一些有用的优化是非常宝贵的,既能拿钱又能获得宝贵经验。咳。。有点扯远了。接下来进入正题。
1、hbase-env.sh中的内存配置
hbase-env.sh中可以配置很多东西,比如hbase的heap大小,hbase的gc策略等等。其实主要就是heap的大小和GC相关的参数。
1)对于heap,也就是HBASE_HEAPSIZE,默认为1G,配置这个,相当于所有的hbase守护进程的heap都使用这个大小,hbase守护进程有这么几个,HMaster、HregionServicer、thrift、Zookeeper相关进程,这里面Zookeeper只的应该是hbase自带的zookeeper,生成环境一般不会使用它,在我们的环境中也不会使用到thrift,那么对于HBASE_HEAPSIZE相当于给HMaster、HregionServicer配置的堆内存大小。
在网上我看到有篇文章说不要直接配置HBASE_HEAPSIZE,因为默认是所有的守护进程都会使用HBASE_HEAPSIZE这么大的内存,对于HBASE_ZOOKEEPER,是内存的浪费。这确实有道理,但在我们系统中并没有启动这些进程,所以暂时可以不考虑每一个守护进程分配不同的内存大小。
我们目前的系统是使用export HBASE_HEAPSIZE=16384,16G的内存,这个数字从哪来呢?相信这还得查看官网,官网不是万能的,但不看官网是万万不能的。一下是官网的一段话:
Thus, ~20-24Gb or less memory dedicated to one RS is recommended
我的英文不是很好,前一句的大概意思是regionserver因为GC的原因不能分配太大的内存,这句就不用我翻译了吧。20~24GB或者更小比较适合。嘿嘿。当然这个参数跟很多因素有关,以后我会再深入总结影响这个内存参数的因素。姑且先这么多。
2)GC配置
不要以为配置了上面的参数就完了,因为你可能会遇到很多情况。比如OOM。为什么?这就要说到java的内存机制了,简要说说吧,以后会有JVM调优的专题。
上图是JVM 分代垃圾收集系统的图表,简要说一下:
这里有 3 个堆分代:Perm(或是 Permanent)代【永久代】,Old Generation 代【老年代】,和 Young 代【年轻代】。年轻代由三个独立的空间组成,Eden 空间和两个 survivor 空间,S0 和 S1。
通常,对象被分配在年轻代的 Eden 空间,如果一个分配失败(Eden 满了),所有 java 线程停止,并且一个年轻代 GC(Minor GC)被调用。所有在年轻代存活的对象(Eden 和 S0 空间)被拷贝到 S1 空间。如果 S1 空间满了,对象被拷贝(提升)到老年代。当这个提升失败,老年代被收集(Major/Full GC)。永久代和老年代通常一起被收集。永久代被用于在存放类和对象中定义的方法。
回到本话题,我们设置GC的参数为
export HBASE_OPTS="$HBASE_OPTS -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=60 -XX:+UseParNewGC -XX:ParallelGCThreads=6"
简要说明一下,
-XX:+UseConcMarkSweepGC 表示年老代并发收集;
对于老年代来说, 它可以更早的开始回收。当分配在老年代的空间比率超过了一个阀值,CMS 开始运行。如果 CMS 开始的太晚,HBase 或许会直接进行 full garbage collection。这种情况会导致block所有的线程,如果这个时间过长,就会导致hbase连接超时,结果就是regionserver集体下线。这是不能容忍额。为了避免这种情况的发生,我们建议设置 -XX:CMSInitiatingOccupancyFraction JVM 参数来精确指定在多少百分比 CMS 应该被开始,正如上面的配置中做的那样。在 百分之 60 或 70 开始是一个好的实践。当老年代使用 CMS,默认的年轻代 GC 将被设置成 Parallel New Collector。
再来看看hbase为什么可能进行full gc,如果我们不配置-XX:CMSInitiatingOccupancyFraction,jdk1.5以后会使用默认值90%,那么很可能,当老年代内存占用超过分配给他的内存大小的90%,会进行CMS(老年代的回收),但是不会阻止年轻代到老年代的迁移,如果迁移过快,CMS较慢,会出现老年代内存使用率100%,这时会导致full gc。如果我们把这个参数调整小一点,那么能给年轻带到老年代迁移的同时做CMS时一些时间,也就减少了full gc的发生。当然这可能会频繁的gc,但总比整个hbase挂掉的好不是么?