可视化监控框架:
Ganglia和JMX,可以查看JVM相关的监控数据。这里暂不作介绍。
垃圾收集器:
因为HBase的优化方案是和具体的垃圾收集器相关的,所以要先了解系统使用的是哪种垃圾收集器。
下面这个链接说了各种垃圾收集器及它们之间的组合关系,一定要看:
http://www.fasterj.com/articles/oraclecollectors1.shtml
可以通过java –XX:+PrintCommandLineFlags –version命令来查看当前JVM的配置信息:
Java8默认使用的还是-XX:+UseParallelGC参数(年轻代使用 PS Scavenge,老年代使用PS MarkSweep)。
垃圾回收优化:
RegionServer因为GC的原因不能分配太大的堆内存,20~24GB或者更小比较适合。
堆的大小通过export HBASE_HEAPSIZE=xxx来设置。
指定新生代的空间:-Xmn512m
年轻代的空间不能太小,也不能太大。太小的话容易在老生代中产生很多碎片,太大的话会使年轻代中的垃圾收集时间过长。HBase官网给出的一个建议是512M。(配置之后可以观察下配置是否合理,如果太小的话你会发现服务器的CPU使用量急剧上升,因为新生代的回收很占CPU)
另外,强烈建议开启JVM的GC日志,开启的配置其实在hbase-env.sh文件中都写了,只是注释掉了,要用的时候直接把注释去掉就可以了:
1、对CMS收集器的优化:
通过设置JRE参数来针对新生代和老年代使用不同的垃圾回收策略:
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC
这里设置年轻带使用Parallel New Collector,这个收集器会停止java进程,但是因为新生代比较小,所以停止的时间是可以接受的。而对老年代,则使用CMS垃圾收集器(尝试在不停止java进程执行的情况下进行垃圾收集,但是在发生Concurrent Mode Failure或Promotion Failure due to Fragmentation的时候依然会导致stop-the-world,所以要尽量避免这两种情况的发生)
Concurrent Mode Failure:在CMS工作时,不断有对象进入持久代,导致持久代空间不足,这时会放弃原本并行执行的垃圾收集,转而使用stop-the-world的复制算法。
Promotion Failure due to Fragmentation:是由老年代内存碎片问题导致的空间不足问题。(因为CMS不会处理内存碎片,所以提前进行CMS并不能解决这种问题,因此要采用MSLAB)
为了避免Concurrent Mode Failure,可以配置以下参数:
-XX:CMSInitiatingOccupancyFraction=70
这个参数的意思是,当老年代的空间分配了70%的时候就开始进行CMS垃圾收集,在一定程度上避免提升失败(默认为90,通常和下面那个参数配合使用?)。
-XX:+UseCMSInitiatingOccupancyOnly:使用CMSInitiatingOccupancyFraction的值作为old区的空间使用率限制来启动CMS垃圾回收。
为了避免Promotion Failure due to Fragmentation,可以开启MSLAB。该特性在0.90.x版本中引入(但默认不开启,需要使用hbase.hregion.memstore.mslab.enabled配置来开启),在0.92版中默认开启(关于MSLAB的介绍,可以参考http://blog.cloudera.com/blog/2011/03/avoiding-full-gcs-in-hbase-with-memstore-local-allocation-buffers-part-3/)。
一个建议的初始配置如下:
export HBASE_REGIONSERVER_OPTS=”-Xmx8g -Xmn128m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:$HBASE_HOME/logs/gc-$(hostname)-hbase.log”
当然,也可以对不同的进程设置不同的JVM参数:
2、对G1收集器的优化:
如果使用的是G1收集器的话(-XX:+UseG1GC),优化的方式会有些不同。
G1收集器相关资料:
http://blog.jobbole.com/109170/
http://product.hubspot.com/blog/g1gc-fundamentals-lessons-from-taming-garbage-collection
下面这些链接针对G1的一些优化配置:
intel的测试和调优:
https://blog.cloudera.com/blog/2014/12/tuning-java-garbage-collection-for-hbase/
官方的博客:这个一定要看
https://blogs.apache.org/hbase/entry/tuning_g1gc_for_your_hbase
官方的博客里对几个方面进行了优化并判断效果:
(a)提高IHOP:-XX:InitiatingHeapOccupancyPercent=xx(默认为45)
设置触发标记周期的 Java 堆占用率阈值。默认占用率是整个 Java 堆的 45%。如果经过测试,发现Tenured区(老年代?)的大小大于IHOP对应的堆大小的话,就会过量地触发标记周期?。这时,可以将IHOP的值适当调高,使其对应的堆大小稍微大于Tenured区大小。
(b)调整SurvivorRatio 和 MaxTenuringThreshold:-XX:MaxTenuringThreshold = 1
MaxTenuringThreshold表示的是垃圾最大年龄,如果设置为0的话,则年轻代对象不经过Survivor区,直接进入老年代。对于老年代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概率。
文中将MaxTenuringThreshold设为了1。
(c)调整G1MixedGCCountTarget 和 G1HeapWastePercent
G1MixedGCCountTarget:设置标记周期完成后,对存活数据上限为 G1MixedGCLIveThresholdPercent 的老年代region执行混合垃圾回收的目标次数。默认值是 8 次混合垃圾回收。Java HotSpot VM build 23 中没有此设置。
G1HeapWastePercent:设置您愿意浪费的堆百分比。如果可回收百分比小于堆废物百分比,则不会启动混合垃圾回收周期。默认值是 10%。Java HotSpot VM build 23 中没有此设置。
每次Mixed GC都会清理1/G1MixedGCCountTarget这么多的high-waste regions (其实就是拥有最多垃圾的region,是从最多垃圾的region开始进行收集的),而且越到后面,收集的region中的存活对象就越多,这就会导致清理时间越来越长(因为要拷贝存活对象,在这篇博客的试验中,从100ms增加到了600ms甚至更长)。为了减少这种极端情况,文中做出了两项调整:
G1HeapWastePercent: default (5) → 10:这个不太理解,原文参考如下:
Allow twice as much wasted space in Tenured. Having 5% waste resulted in 6 of the 8 potential Mixed GC events occurring in each Mixed GC cycle. Bumping to 10% waste should chop off 1-2 more of the most expensive events of the Mixed GC cycle.
G1MixedGCCountTarget: default (8) → 16:增加了每次Mixed GC流程中清理的region的次数,但是每次清理的工作量减少为了原来的一半,因此单次清理时间应该是会下降的。
(d)调整Heap Size 和 HBase Configs
观察过去一个月集群中block cache、memstore、static index的最大值,并将这些值乘上1.1作为我们下一步配置的数据(如:10G变为11G)。
设置Eden区的大小:-XX:G1NewSizePercent = 8(起始为8%)。
然后根据如下公式计算heap size:
Heap ≥ (M + B + O + E) ÷ 0.7
M = memstore cap, GB
B = block cache cap, GB
O = static index cap, GB
E = Eden size, GB
计算完后通过以下参数设置heap的大小:
-Xms40960m -Xmx40960m
接着要调整之前设置的IHOP:
IHOP = (memstore cap’s % heap + block cache cap’s % heap + overhead cap’s % heap + 20)
(其中的overhead就是指static index)
-XX:InitiatingHeapOccupancyPercent = IHOP
接着是对HBase进行一些配置(hbase-site.xml):
hfile.block.cache.size → block cache cap ÷ heap size
hbase.regionserver.global.memstore.size → memstore cap ÷ heap size
(e)调整Increase G1 Region Size:-XX:G1HeapRegionSize = #M
官方建议G1收集器中region的数量在2048个以内较为合适,默认的region大小为16M,因为我们调大了heap,因此最好适当调大region的大小。文中将region的大小从16M调整为了32M(region的大小必须为2的指数)。
其他HBase JVM优化相关资料:
1、下面这个链接相当于是对《HBase权威指南》性能优化一章的翻译
http://www.thebigdata.cn/HBase/15677.html
2、官方博客三篇:Avoiding Full GCs in Apache HBase with MemStore-Local Allocation Buffers: Part 1\2\3
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/
总结:
不同的集群配置、虚拟机都有不同的优化方法,在进行优化前最好根据监控框架给出的各种数据(GC类型、频率、每次GC时间等,强烈建议参考https://blogs.apache.org/hbase/entry/tuning_g1gc_for_your_hbase),分析某些地方是否存在性能问题,然后再进行相应的优化测试。