HBase写入优化

来源:互联网 发布:网络大电影市场前景 编辑:程序博客网 时间:2024/05/30 23:02

前言

在HBASE持续写入的时候遇到一种奇怪的情况,写着写着HBase就会出现一阵写入速度为0的情况。在网上查了很多资料,终于找到一篇外文的资料,有详尽的实验和解决方案,在此做一下翻译。原文连接 (需要翻墙)

翻译

问题

我们的程序是通过mapreduce job运行hive query。但mapper程序运行的时候会出现失败(不断的重复失败,最终导致job被kill),报错如下
java.io.IOException: org.apache.hadoop.hbase.client.ScannerTimeoutException: 63882ms passed since the last invocation, timeout is currently set to 60000

我们做了一些研究发现,这个异常是scanner的next方法没有在超时限制的时间内被调用导致的。我们用一个简单的复制table操作重现了这个错误,又通过hive 执行select overwrite table A select * from table B重现了同样的错误。在两个案例里mapper程序被指定基于TableInputFormat(就像我们的Hive job)scan一个split,mapper只是简单的扫描并且把记录写入新table。由于写入操作被暂停,所以scanner的next方法没有被调用,并出发了超时异常。

日志

第一站——日志。regionserver的日志里记录了如下的log
WARN org.apache.hadoop.hbase.regionserver.MemStoreFlusher: Region occurrence,\x17\xF1o\x9C,1340981109494.ecb85155563c6614e5448c7d700b909e. has too many store files; delaying flush up to 90000ms
INFO org.apache.hadoop.hbase.regionserver.HRegion: Blocking updates for ‘IPC Server handler 7 on 60020′ on region occurrence,\x17\xF1o\x9C,1340981109494.ecb85155563c6614e5448c7d700b909e.: memstore size 128.2m is >= than blocking 128.0m size

阻止刷新一段时间,这会导致停止写入后续记录并阻止调用scanner.next()。经过更多的研究(并测试了将hbase.client.scanner.caching设为1在内的各种不同的值)我们认为这就是需要解决的问题,但是这个问题是怎么发生的呢?

为什么它阻止写入

阻止写入的原因是memstore已经达到了我称之为memstore blocking limit的限制。这个限制的大小是由hbase.hregion.memstore.flush.size *hbase.hregion.memstore.block.multiplier计算出来的,默认情况下是64M*2。正常情况下memstore达到flush.size时会被写入storefile,但是当memstore达到128M并且store file太多时memstore就不能再写入到store file中了(看上面log的第一行)。“too many storefiles”是由hbase.hstore.blockingStoreFiles 参数控制的(默认是7)。当一个memstore flush的时候就会产生一个新的store file,hbase的minor和major compaction机制会负责将这些store file 合并生成更大的store file,以减少storefile的数目。默认情况下compaction会在store file数目大于hbase.hstore.compactionThreshold (默认值是3)并小于hbase.hstore.compaction.max(默认值是7)的情况下被触发。另外当memstore在regionserver中合并时使用了太多的堆内存后,无论flush.size被设成什么值,memstore都会flush。可接受的heap设置是由设置hbase.regionserver.global.memstore.lowerLimit(默认0.35) 和hbase.regionserver.global.memstore.upperLimit(默认0.4)。有一个线程专门负责有规律的flush并检查这些限制。

  • 如果flush线程苏醒,并且memstore的数目大于最小限制,它会开始flush(从当前最大的memstore开始)知道memstore的数目低于最小限制。
  • 如果flush线程苏醒,并且memstore数目大于上限的限制,它会阻止更新并且开始flush,直到memstore的数目小于下线的限制。

幸运的是我们没有看到由超过heap上限设置导致的block,只是遇到了前面描述的“memstore blocking limit”。但是目前为止我们只知道我们可以调整的不同参数。

停止block

我们的目标是停止block,并且不会导致regionserver超出内存设置,这样我们的mapper才能不超时。最明显的问题是我们有太多的storefile了,以至于不能及时的把它们进行combination。注意我们有6G的内存分配给hbase,不能在增加了。

我们从提高memstore flush的大小开始, 这会在每次flush的时候产生更少但是更大的storefiles:

  • 首先我们尝试了128M的flush size,保持block multiplier的值为2。但是不能解决问题,他仍然产生太多的storefile,并导致同样的block(略好于64M的时候)
  • 然后我们尝试了256M的flush size,并将block mulitplier的值设为4。log和ganlia都显示在storefile达到256M前flush就发生了(大约达到130M的时候)“due to global heap pressure”这表示storefile消耗了太多的heap内存。这以为这我们还是产生了太多的文件并且仍然有相同的block问题,但是随着“memstore blocking limit”被设成1GB后(256M*4),memstore block反生的频率已经低很多了,尽管仍然导致mapper被kill。

我们现在虽然产生了更少的storefile,但是他们仍然堆积的太快了。从ganlia我们看到压缩对联和storefile counts 无限制的增长,这意味着我们经常达到block限制。下一步我们准备尝试每次合并更多的文件,因此我们增大到compaction.max的值到20,这回有了一些不同。

如何减少storefile的数目呢?如果我们有更少的store , 我们需要创建更上的文件并且减少memstore使用的heap内存,下一步我们增加了region的大小。这需要增加hbase.hregion.max.filesize的大小,从256M提升到了1.5G,并且使用更少的pre split重建了我们的table。这导致我们的region减少了75%的region。

然后一切看起来都好了,“Blocking updates”的在单次运行的时候出现的很少了,但是它仍然会导致1-2个job会被kill。我们尝试了提高memstore.lowerLimit和upperLimit 到0.45和0.5,但是没有什么效果。

现在怎么办

情况看起来有点糟糕。经过不断的盯着ganlia的图,我们不断的回顾到一个没有被解释的由storefile最终导致kill job的点。

hbase_write_flush

大约在job跑到一半的时候,storefile的大小会停下,并且逐步的增长知道job死掉。注意这张图显示的是平均值,hbase会保留很少flush以等待storefile达到1G然后开始block,这是我们job死掉的原因。回到log。

从图的开始阶段我们看到线很平滑,memstore在正好达到或者是略微超过256M的时候写入storefile,这意味着memstore有足够的内存。从log里我们看到flush进行的很好,但是有下面这种log。

INFO org.apache.hadoop.hbase.regionserver.MemStoreFlusher: Under global heap pressure: Region uat_occurrence,\x06\x0E\xAC\x0F,1341574060728.ab7fed6ea92842941f97cb9384ec3d4b. has too many store files, but is 625.1m vs best flushable region’s 278.2m. Choosing the bigger.

这并没有“delaying flush”的log那么糟糕。但是它显示出我们的heap刚好能处理flush。然后在12:20左右,我们看到越来越多的下面的log

WARN org.apache.hadoop.hbase.regionserver.MemStoreFlusher: Region uat_occurrence,”\x98=\x1C,1341567129447.a3a6557c609ad7fc38815fdcedca6c26. has too many store files; delaying flush up to 90000ms

然后这些log结束在下面的log出现的时候

INFO org.apache.hadoop.hbase.regionserver.wal.HLog: Too many hlogs: logs=35, maxlogs=32; forcing flush of 1 regions(s): ab7fed6ea92842941f97cb9384ec3d4b
INFO org.apache.hadoop.hbase.regionserver.MemStoreFlusher: Flush thread woke up with memory above low water.

因此现在是heap的太小了导致memstore在一开始的时候就被强制flush了(storefile增加的比我们能合并的多)。然后就会出现由于太多的storefile延迟flush(memstore开始变大——图里面显示的),然后(WAL)开始在log中抱怨产生了太多的log,这导致强制flush storefile(这样WAL HLog才能被安全的丢弃——这又增加了stroefile的数目)。flush thread苏醒后,发现已经超出heap限制了,开始尝试flush,这仅仅是加重了问题(增加了更多的storefile)。job失败不会马上发生但是却没有任何输出,大约13:00的时候memstore开始达到“memstore blocking limit”最终mapper死掉。

我们应该做什么?

在memstore flush 大小的分析过程中知道发生了什么值得欣慰,但是这仅仅增强了我们已经知道的事实——问题的症结在storefile太多。所以我们要做什么呢?提升storefile的最大数目,这就是方法!每个storefile在store中都消耗资源(file handles, xceivers,存储metadata的heap),至就是为什么file store的数目默认被限制在7上。但是对我们这种写的压力来说,这个值太小了、在一般的操作中我们不会达到这种规模的写入,有足够的能力将一组storefile一次性写入,间歇性的爆发相对安全,因为我们知道晚上运行的major compaction会将多出的store file清理干净(从而释放掉所有额外的资源)。我们将这个最大值设为了200,最终程序运行正常了!

总结

我们的问题是我们的compaction不能处理所有生成的storefile。我们尝试修改了各种hbase参数去解决这个问题,但是最后最后解决这个问题的关键是将hbase.hstore.blockingStoreFiles 设成200,这个值可能2倍于我们真正需要的大小,但是可以留给一些不经常做的需要大量写入的job一些缓冲空间。另外我们设置了更大的region,还有比默认更大的memstore大小。下面是我们修改hbase-site.xml 的一些片段。

<!-- default is 256MB 268435456, this is 1.5GB -->  <property>    <name>hbase.hregion.max.filesize</name>    <value>1610612736</value>  </property>  <!-- default is 2 -->  <property>    <name>hbase.hregion.memstore.block.multiplier</name>    <value>4</value>  </property>  <!-- default is 64MB 67108864 -->  <property>    <name>hbase.hregion.memstore.flush.size</name>    <value>134217728</value>  </property>  <!-- default is 7, should be at least 2x compactionThreshold -->  <property>    <name>hbase.hstore.blockingStoreFiles</name>    <value>200</value>  </property>

最后,如果我们的compaction能够更快或者更频繁,我们可能就能跟上storefile的创建了。但是如果不采取多线程compaction的方法看起来就不太可能实现,这个需求已经在新版本的hbase中有所提及,因此如果你有这类问题,一个升级可能就能解决,这次确实提示我们去考虑升级到CDH4.


0 0