一图胜千言,先来看下HBase数据写入流程:

hbase查看写入速度 hbase写入慢问题_hbase

如上图所示,当数据写到服务端时,在持久化到磁盘之前,要经过三个重要过程:

  • 追加写WAL日志:数据会首先追加写入到WAL文件,用于故障恢复。
  • 写入MemStore:然后写入所属Region的MemStore缓存中,此时客户端写入就算成功了。
  • MemStore Flush:当MemStore达到一定阈值,或者满足一定条件就会Flush到磁盘,生成一个HFile文件。

在此过程中,我们经常会遇到写入阻塞问题,表现为数据无法写入,本文我们就来分析可能会引发写入阻塞的几种情况,以及如何尽量避免阻塞问题。

HFile级别的阻塞

如上所述,数据写入过程中,MemStore在满足一定条件时会flush刷写到磁盘,生成一个HFile文件。当一个Store下的HFile文件数量大于某个阈值时,就会引起写入或更新阻塞。RS日志中会有类似“has too many store files...”的信息。当出现这种情况时,需要等待Compaction合并以减少HFile数量,这里主要是Minor Compaction即小合并。涉及的主要参数有:

  • hbase.hstore.blockingStoreFiles
  • hbase.hstore.compaction.min
  • hbase.hstore.compaction.max
  • hbase.regionserver.thread.compaction.small
  • hbase.regionserver.thread.compaction.large

这几个参数默认值都有点小,可以根据实际场景调整,针对hbase.hstore.blockingStoreFiles这个参数,HBase 1.x的默认值是10(2.x调整到了16),通常建议调大点比如100,尽量避免写入阻塞。另外几个参数也可以适当调大,参数含义与调整建议可以参考《从原理到参数解析,HBase刷写与合并机制介绍》这篇文章。

MemStore阻塞倍数

当MemStore大小达到刷写阈值(hbase.hregion.memstore.flush.size,默认128M)时,就会flush刷写到磁盘,这个操作基本没有阻塞。但当一个Region的所有MemStore大小达到一个阻塞倍数(hbase.hregion.memstore.block.multiplier,默认值为4,即4倍的刷写阈值 默认4*128=512M)时,就会阻塞该Region所有的更新请求,并强制flush。客户端可能会抛出RegionTooBusyException异常。涉及参数包括:

  • hbase.hregion.memstore.flush.size
  • hbase.hregion.memstore.block.multiplier

为了尽量避免写入阻塞,可以适当调整这两个参数,比如当数据写入过快,并且服务端内存充裕时,我们可以把刷写阈值调大到256M,阻塞倍数可以不调或调到5~8,并观察实际刷写或阻塞的情况。

RegionServer级别的阻塞

最后则是RegionServer(简称RS)级别的限制,一个RS中所有的MemStore有一个总大小限制和低水位阈值。前者受限于一个global memstore size参数,默认为0.4即一个RS中所有MemStore总大小最多占RS堆内存的40%,达到该阈值写入请求就会被阻塞,此时RS日志会有类似"Blocking updates...the global memstore size..."的信息;后者由一个lower limit参数控制,默认为0.95,表示达到前者的95%时会强制flush一些MemStore,使得写缓存总大小维持在一个低水位以下。

另外一个RS堆内存除了上述的40%外,读缓存BlockCache也占据了40%,这二者比例可以相互调整,在一些重写轻读场景中我们可以适当增加写缓存、减少读缓存的比例,这样可以尽量避免写阻塞。涉及参数包括:

  • RegionServer Heap 
  • hbase.regionserver.global.memstore.size
  • hbase.regionserver.global.memstore.size.lower.limit
  • hfile.block.cache.size

除了在一些场景下读写缓存比例可以适当调整外,RS堆自身内存的设置也比较重要,HBase进程默认只有1G大小的堆,生产环境中肯定不够用,调整RS堆大小需要参考集群规模、负载情况等,另外调整RS堆的同时可能还涉及JVM的调整,比如选择什么样的垃圾收集器、JVM参数等。

小结

本文主要从HFile级别、MemStore或者说Region级别、RegionServer级别等三个方面,分析了几种可能会导致写入阻塞的情况,给出了一些关键参数,以及如何调整才能尽量避免写入阻塞。