这个测试分析由淘宝网架构师毕玄给出的,原文参考:http://blog.bluedavy.com/?p=236, 从中我们可以看到Hbase的配置参数的无穷魅力,通过参数的调整能够使Hbase更加适合我们的具体业务场景。
================分割线======================
Facebook在谈到其选择HBase作为新的message体系的存储时,提到了一点是hbase的高性能写,那hbase的写性能状况到底如何了,还是需要靠测试来说明,在这篇blog中就以我们目前做的一些测试来看看hbase的写速度,以及分享下我们对于测试中体现出来的数据的分析和测试时碰到的一些问题,还希望对hbase有研究或实践的同学多加指正。
测试环境:
1、4个client,每client 25个写线程,每个client写入1kw数据,其他还有一些更长时间的测试,例如连续写7个小时等;
2、12台region server(8 core 超线程 => 16 core, Intel Xeon E5620, 12块1T的Sata盘,没做raid, 24g, 12g heap);
3、HBase: 0.90.2, Hadoop: 0.20.2 CDH3U0。
说明:
1、由于我们的场景中要求数据写入的绝对可靠,因此在HTable的基础上增加了一个写安全的方法:putAndCommit,代码如下:
所有的测试场景均采用这个方法进行写入。
2、写入时的key均为120字节,value为1024字节,key和value均为[A-Z,0-9]的随机组合;
3、由于YCSB写的其实一般,于是我们自行写了一个用于做hbase读写测试的类,可支持多table/多cf/多列,不同key/value size,不同读写线程比,不同读cache命中率的测试。
根据测试的状况,最终我们对写速度进行了6种不同场景的测试,以判断写速度的影响因素以及波动的影响因素,最终形成了以下的两个测试结果图:
其中X轴代表程序的运行时间,Y轴代表平均响应时间,单位是ms。
第一个测试用例为默认参数的情况,它代表的为client以及server均为默认参数,从上面的图中可以看出在这种测试场景下写速度的波动范围比较大,从平均的4ms到偶尔的500ms+都会存在,并且在长时间的测试下还会出现长达几秒的状况,对于online性质的业务而言,这显然是不可接受的,在做这个测试的时候,还碰到了hbase的一个典型问题,就是写空表时压力在很长一段时间内都会集中在一台region server上,没有办法很好的发挥集群的能力,这种情况下可以选择采用0.90.2以后支持的pre-sharding的方式,但挺难用的,我们目前采用的方法是自己增加了一个将table的region平均分散到各region server的管理功能,这样可以通过手动的方式来充分发挥集群的能力,当然,这还是要求在写的时候是分散的,如果写连续key的话还是会导致在同一台,这是在设计hbase rowKey时要仔细考虑的。
因此需要想办法避免写速度的波动,下面的测试基本都是围绕这个宗旨而进行的,根据之前对hbase client代码的阅读,client在写时会进行多次的重试,重试的间隔采用的是指数避让的方法,间隔时间为1秒为单位,在默认10次重试的情况下,就会是这样的间隔时间:[1,1,1,2,2,4,4,8,16,32],从这可以看出当server出现异常时会导致client长时间的等待,因此决定将client的重试间隔时间调短为20ms(hbase.client.pause),重试次数调整为11次(hbase.client.retries.number),保证客户端会在2s+的范围内进行重试,顺带将hbase.ipc.client.tcpnodelay设置为true,同时将ipc.ping.interval也调整为3s,避免长时间的等待,这样调整后测试的结果如上图所示,可以看到这时写速度仍然是波动的,但波动的范围就好多了,基本会在100ms以下,也算是有所进步了。
结合client代码来看,写速度仍然波动的话从client部分已经很难有所调整了,因此要从server端想办法了,根据之前对server代码的分析以及上面两测试时server的日志,初步判断造成很大波动的主要原因是split,于是决定关闭split进行测试,我们将hbase.regionserver.regionSplitLimit设置为1来关闭split,根据上面图中关闭split的测试结果,可以看到在不做split时,写速度的波动频率比以前小了很多,同时波动的范围也减小了,但仍然在波动。
继续分析server日志,看到的一个主要问题是随着写入数据越来越多,server会compact出越来越大的文件,于是compact的时候磁盘io很吃紧,会造成影响,因此决定再关闭compact,关闭compact的方法为将hbase.hstore.compactionThreshold调整为Integer.MAX_VALUE,在关闭compact的第一次测试中,发现波动范围更大,查看日志发现会有偶尔的“Blocking updates for ”和“delaying flush up to ”情况,结合region server代码发现Memstore在flush region时会先检查store中store file是不是太多了,太多了则会先进行compact,并将flush推迟,而flush推迟的过程中,会导致memstore中写了很多的数据,在memstore的大小大于了其两倍的空间(默认情况下也就是128m)时,会阻塞住此时的写/删除请求,等到memstore大小降下去后才会恢复,于是相应的对这两个参数进行了调整,hbase.hstore.blockingStoreFiles调整为了Integer.MAX_VALUE,hbase.hregion.memstore.block.multiplier调整为了8,调整完毕后进行关闭compact/split的测试才得到了上面图中的测试结果,从图中的测试结果来看,写的波动频率比仅关闭split时又降低了,波动范围基本在20ms以下,到此算是不错了。
题外话:在另外一个一台region server上很多region的测试下,发现即使关闭了compact/split,调整了上面的参数后,仍然会有很大波动的情况下,分析后发现主要是因为region太多,导致memstore加起来的大小很快超过了默认的0.35 * heap的阈值,在这种情况下会导致要阻塞进行刷新,对于这种情况只能是降低region数或者是调小memstore的大小;另外一个原因则是hlog太多,默认情况下当hlog有32个时,就会要对所有region的memstore进行flush,而不管这个时候memstore使用了多少,悲催的是memstore的flush是串行的,500个可想而知这个影响就大了,这种情况下可以调大hlog最大的个数,但其实作用也不大,因此看起来在默认情况下hbase单个region server其实是不太适合跑很多region的,当然,这取决于写频率,写频率低的话倒也OK的。
回到主题,继续,到这步了,写速度还是波动,因为memstore flush的过程其实影响不会太大,毕竟只是创建一个snapshot,因此猜想莫非写速度仍然波动是写hlog造成的,于是决定关闭写hlog来试试,这样就有了上面结果图中的关闭hlog的测试结果了,从图中可以看到关闭写hlog后波动频率比默认情况下确实有下降,说明写hlog确实会造成波动。
最后决定结合前面的经验,关闭写hlog以及compact/split进行终极测试,可以看到这时尽管写速度仍然有波动,但波动频率很低,波动范围也很小了。
在测试中还碰到两个问题是比较烦恼的:
1、由于region server是多台,想找到client写速度波动时server到底什么状况还是比较麻烦的;
2、如果测试环境公用,可能会有干扰的问题,最主要的是要知道每个region server的请求到底是在做什么表的操作或来源于什么IP的请求。
根据以上的测试,最后总结为:
1、对于写速度而言,影响因素的效果主要为: 写hlog > split > compact;
ps: (从我们用0.20.2-branch-append的官方版本测试来看,写hlog的速度可以大幅度提升,但由于这版本没release,处于数据安全性,大家还是先用cloudera版本吧。)
2、对于写速度波动而言,想完全不波动是不可能滴,影响因素的效果主要为:split > 写hlog > compact;
3、对于写频率较高的应用而言,一台region server上不适合有太多的region,到底多少这个和每行数据的大小,机器的内存有关;
4、Pre-Sharding可以不做,而改为靠手工move以及合理的row key设计来做到分布到region server上,从move这点来看hbase做在线扩容和热点分布调整还是相当方便的;
5、对于没有delete类型的业务而言,其实关闭compact/split也没什么不行的,等到加机器的时候再做下就行了;
6、对于数据安全性要求不是那么高的应用而言,关闭hlog会获得很大的回报