文/刘星
HBase作为BigTable的一个开源实现,随着其应用的普及,用户对它的性能数据愈发关注。本文将为您揭开HBase性能测试的一角,邀您一起参与到对云计算模块性能调优的深度思考中。
对于BigTable类型的分布式数据库应用来说,用户往往会对其性能状况有极大的兴趣,这其中又对实时数据插入性能更为关注。HBase作为BigTable的一个实现,在这方面的性能会如何呢?这就需要通过测试数据来说话了。
数据插入性能测试的设计场景是这样的:取随机值的Rowkey长度为2000字节,固定值的Value长度为4000字节,由于单行Row插入速度太快,系统统计精度不够,所以将插入500行Row做一次耗时统计。
这里要对HBase的特点做个说明,首先是Rowkey值为何取随机数,这是因为HBase是对Rowkey进行排序的,随机Rowkey将被分配到不同的region上,这样才能发挥出分布式数据库的性能优点。而Value对于HBase来说不会进行任何解析,其数据是否变化,对性能是不应该有任何影响的。同时为了简单起见,所有的数据都将只插入到一个表格的同一个Column中。
初次分析
在测试之初,需要对集群进行调优,关闭可能大量耗费内存、带宽以及CPU的服务(例如Apache的HTTP服务),保持集群的宁静度。此外,为了保证测试不受干扰,HBase的集群系统需要被独立,以保证不与HDFS所在的Hadoop集群有所交叉。
实验
那么做好一切准备之后,就开始进行数据灌入,客户端从Zookeeper上查询到Regionserver的地址后,开始源源不断地向HBase的Regionserver上喂入Row。
这里,我写了一个通过JFreeChart来实时生成图片的程序,每3分钟,喂数据的客户端会将获取到的耗时统计打印在一张十字坐标图中,这些图又被保存在制定的Web站点中,并通过HTTP服务展示出来。在通过长时间不间断的测试后,我得到了图1。
图1好似一条直线上,每隔一段时间就会泛起一个波浪,且两个高峰之间必有一个较矮的波浪。高峰的间隔呈现出越来越大的趋势,而较矮的波浪恰好处于两高峰的中间位置。
解读
为了解释,我对HDFS上HBase所在的主目录下文件,以及被插入表格的region情况进行了实时监控,以期发现这些波浪上发生了什么事情。
回溯到客户端喂入数据的开始阶段,创建表格,在HDFS上便被创建了一个与表格同名的目录,该目录下将出现第一个region,region中会以family名创建一个目录,这个目录下才存在记录具体数据的文件。同时在该表表名目录下,还会生成一个“compaction.dir”目录,该目录将在family名目录下region文件超过指定数目时用于合并region。当第一个region目录出现时,内存中最初被写入的数据将被保存到该文件中,这个间隔是由选项“hbase.hregion.memstore.flush.size”决定的,默认是64MB,该region所在的Regionserver的内存中一旦有超过64MB的数据时,就将被写入到region文件中。这个文件将不断增殖,直到超过由“hbase.hregion.max.filesize”决定的文件大小(默认是256MB,此时加上内存刷入的数据,实际最大可能到256MB+64MB)时,该region将被执行split,立即被一切为二,其过程是在该目录下创建一个名为“.splits”的目录作为标记,然后由Regionserver将文件信息读取进来,分别写入到两个新的region目录中,最后再将老的region删除。这里的标记目录“.splits”可以避免在split过程中发生其他操作,起到类似于多线程安全的锁功能。在新的region中,从老的region中切分出的数据独立为一个文件并不再接收新的数据(该文件大小超过了64MB,最大可达到(256+64)/2=160MB)),内存中新的数据将被保存到一个重新创建的文件中,该文件大小将为64MB。内存每刷新一次,region所在的目录下就将增加一个64MB的文件,直到总文件数超过由“hbase.hstore.compactionThreshold”指定的数量时(默认为3),compaction过程就将被触发了。在上述值为3时,此时该region目录下,实际文件数只有两个,还有额外的一个正处于内存中将要被刷入到磁盘的过程中。Compaction过程是HBase的一个大动作。HBase不仅要将这些文件转移到“compaction.dir”目录进行压缩,而且在压缩后的文件超过256MB时,还必须立即进行split动作。这一系列行为在HDFS上可谓是翻山倒海,影响颇大。待Compaction结束之后,后续的split依然会持续一小段时间,直到所有的region都被切割分配完毕,HBase才会恢复平静并等待下一次数据从内存写入到HDFS。
理解了上述过程,就必然会对HBase的数据插入性能为何是图1所示的曲线的原因一目了然。与X轴几乎平行的直线,表明数据正在被写入HBase的Regionserver所在机器的内存中。而较低的波峰意味着Regionserver正在将内存写入到HDFS上,较高的波峰意味着Regionserver不仅正在将内存刷入到HDFS,而且还在执行Compaction和Split两种操作。如果调整“hbase.hstore.compactionThreshold”的值为一个较大的数量,例如改成5,可以预见,在每两个高峰之间必然会等间隔地出现三次较低的波峰,并可预见到,高峰的高度将远超过上述值为3时的高峰高度(因为Compaction的工作更为艰巨)。由于region数量由少到多,而我们插入的Row的Rowkey是随机的,因此每一个region中的数据都会均匀的增加,同一段时间插入的数据将被分布到越来越多的region上,因此波峰之间的间隔时间也将会越来越长。
再次理解上述论述,我们可以推断出HBase的数据插入性能实际上应该被分为三种情况:直线状态、低峰状态和高峰状态。在这三种情况下得到的性能数据才是最终HBase数据插入性能的真实描述。那么提供给用户的数据该是采取哪一个呢?我认为直线状态由于所占时间会较长,尤其在用户写入数据的速度也许并不是那么快的情况下,所以这个状态下得到的性能数据结果更应该提供给用户。
再度分析
前面的HBase性能深度分析,提出了一个猜想,是关于调整“hbase.hstore.compaction-Threshold”值的假设。猜想的内容为:如果改变该值,例如调整为5,那么耗时图形会在每两个高峰之间出现等间隔的三次较低的波峰,并且高峰将会更加突出,超过上述值较低时的波峰高度。
为了证明这个猜想,我将“hbase.hstore.compactionThreshold”值调整为5,并重新做了数据插入测试,一段时间后,得到如图2所示的性能图形(Y轴表示耗时,X轴为插入次数,Sandy建议这里的Y轴应该改为插入速度,但是由于前次已经使用了耗时为Y轴,因此改变Y轴显示的工作只能放到下次测试中了)。
通过相比发现,图1和图2的Y轴比例尺是不同的,图1中Y轴最大为30秒,图2中最大为50秒。可见假设中声称低峰会在两个高峰之间等间隔的出现3次的现象的确是成立的。当高峰出现第5次以后,可以从图2看到代表耗时的点的高峰段已经达到了25秒以上,而对于前次来说,高峰段基本上处于20秒左右,由此可以认为Compaction的压力的确是增加了。现在换一个角度来分析这一情况。我为图1制作了一张数据分布图(图3),与图2进行比较(图4)。
虽说第二次测试经历的时间不如第一次,但是基于统计学的观点,分布图的外形是不会受样本容量大小影响的,因此图3和图4可以进行外观上的比较。这两张分布图都是典型的正态分布图,但又不是标准正态分布,原因在于,波峰段的数据影响了正态分布的标准性,表现之一在于右侧的长尾,表现之二在于众数所在位置右移,以至于左侧凸显了一个小波峰。
计算本次分布图与前次分布图中位于右侧长尾部分的数据的标准差(计算公式:),我们可以得到前次右侧标准差为4.10390692438,而本次右侧标准差为7.12068861446,说明高峰段影响的数据右偏更为严重了。
从外观上表现在右侧长尾在整图比例尺中的宽度和高度要大于前次分布图中的右侧长尾。这说明Compaction的压力增大了。
推导到这里,我发现右侧标准差与Compaction的压力之间是存在显著关系的,今后对Compaction压力增减的估算,貌似可以转换为对右侧标准差的计算。压力增加的比率是否等于标准差的比值呢?这里先做一个标记,等后面有时间再仔细思考一下这个问题。现在假设中的说法“高峰将会更加突出,超过上述值较低时的波峰高度”,应该算是被证明了。
以上论证结束之后,按照惯例还是要提出一些假设和推断。
HBase在已经发布的0.90.x版对Compaction和Split机制作了调整,将Split过程提到了Compaction之前,也就是说,当region目录下,HFiles数目超过“hbase.hstore.compactionThreshold”指定值之后,Regionserver会首先计算一下Compaction之后的文件大小是否会超过“hbase.hregion.max.filesize”确定的Split上限大小,如果超过了,那么HFiles首先被切分,然后才会将切分好的文件转移到新的region中Compaction。这样将大大减小Compaction的压力,由此可以推断,HBase的性能调优必然与“hbase.hstore.compactionThreshold”和“hbase.hregion.max.filesize”这两个值的大小息息相关。理论上,可以将某次设定了确定值的实验中获得的数据代入到一个特定公式中,上述两值作为该公式的自变量,其应变量,即性能数据,将可以轻松地计算出来。
是否真的如此,且待进一步的详细测试。