HBase Memstore配置

HBaseMemstore配置

本文为翻译,原英文地址:http://blog.sematext.com/2012/07/16/hbase-memstore-what-you-should-know/

当regionserver(以下简称RS)收到一个写请求,会将这个请求定位到某个特定的region。每个region存储了一系列的row,每个row对应的数据分散在一个或多个ColumnFamily(以下简称CF)中。特定CF的数据都存储在对应的store里面,而每个store都由一个memstore和数个storefile组成。memstore存储在RS的内存中,而storefile则存储在HDFS上。当一个写请求到达RS的时候,该请求对应的数据首先会被memstore存储,直到达到一定的临界条件(显然,主存需要良好的控制),memstore里面的数据才会flush到storefile。

使用memstore的主要原因是为了使存储在DFS上的数据是有序的(按row)。DFS设计为顺序读写的,已有的文件不能被修改。这就意味着,因为hbase收到的写请求是无序的,所以如果直接将这些数据写到DFS上,以后再对文件里面的内容做排序就是一件极其困难的事情;无序的数据存储方式,又会大大影响后续的读请求性能。为了解决这种问题,hbase会将最近的某些写请求放到内存中(也就是memstore),并将这些数据在flush到storefile之前做好排序。

当然,除了解决排序的问题,memstore还有其他很多额外的好处,比如:

它能充当memcache的角色,缓存最近写入的数据。鉴于新数据的访问频率和几率都比一般比旧数据高很多,这就大大的提高客户端的读效率。

在数据持久化到DFS之前,还能做一些其他的优化操作。比如,某一个row下面提交了多次数据,而实际上根据表设计,我们只需要持久化存储其中最近一次或几次的内容,在刷新之前,通过memstore就可以把多余的数据屏蔽,提高写数据的性能。

注意,每个memstore每次刷新时,都会给每个CF生成一个storefile.

剩下读取就非常容易了,hbase会检查数据是否在memstore里面,否则就去storefile读取,然后返回给客户端。.

WhattoCareabout

为了更好的使用hbase,开发人员和管理人员必须了解memstore是什么以及它如何工作,因为:

有很多与memstore相关的配置选项会大大提升hbase运行效率,避免出现问题。但是hbase本身不会根据你的使用方式和场景自动优化参数。

频繁的memstoreflush会影响到读取的性能,也会给系统带来额外的开销。

memstore的flush机制会影响你的数据表设计。

让我们进一步的说明下这些问题。

ConfiguringMemstoreFlushes

一般来说,有两类配置选项(先不关注由于region关闭导致的强制刷新):

首先,何时应该触发flush?

其次,什么时候触发的flush会block写请求

第一类是关于“常规的”flush,这一类flush触发时还能并行的响应写请求。这一类的配置如下:

hbase.hregion.memstore.flush.size

<property>

<name>hbase.hregion.memstore.flush.size</name>

<value>134217728</value>

<description>

memstore的大小超过该限制(单位:byte)后将被flush到磁盘。这个大小由一个线程间断性的检查,检查的间隔由

hbase.server.thread.wakefrequency决定

</description>

</property>

base.regionserver.global.memstore.lowerLimit

<property>

<name>hbase.regionserver.global.memstore.lowerLimit</name>

<value>0.35</value>

<description>

一个RS中所有的memstore的总容量超过堆的该百分比限制后,将被强制flush到磁盘。

Maximumsizeofallmemstoresinaregionserverbeforeflushesareforced.Defaultsto35%ofheap.这个值与

hbase.regionserver.global.memstore.upperLimit相等,以减小由于到达该值触发flush的几率,因为这种flush会block写请求

</description>

</property>

注意,第一条设置是针对于每一个memstore。设置该值的的时候,你需要计算一下每台RS上的region数量。随着RS的数量增加(而且你只在RS数量较少的时候设置了该值),memstoreflush可能会由于第二条设置提前触发。NotethatthefirstsettingisthesizeperMemstore.I.e.whenyoudefineityoushouldtakeintoaccountthenumberofregionsservedbyeachRS.WhennumberofRSgrows(andyouconfiguredthesettingwhentherewerefewofthem)Memstoreflushesarelikelytobetriggeredbythesecondthresholdearlier.

第二类的设置主要是针对安全性的考虑。有的时候写负载非常高,flush的速度会跟不上。我们也不想memstore无限制的增长,因此这种情况下,写请求会被阻塞,直到memstore回归到一个可控的大小。这些临界值配置如下:Secondgroupofsettingsisforsafetyreasons:sometimeswriteloadissohighthatflushingcannotkeepupwithitandsincewedon’twantmemstoretogrowwithoutalimit,inthissituationwritesareblockedunlessmemstorehas“manageable”size.Thesethresholdsareconfiguredwith:

hbase.regionserver.global.memstore.upperLimit

<property>

<name>hbase.regionserver.global.memstore.upperLimit</name>

<value>0.4</value>

<description>

一个region中所有memstore的总大小超过堆的该百分比限制时,会发生强制flush,并block更新请求。

默认是堆大小的40%。更新会被阻塞,并发生强制flush,直到所有memstore的大小达到

hbase.regionserver.global.memstore.lowerLimit的限制。

</description>

</property>

hbase.hregion.memstore.block.multiplier

<property>

<name>hbase.hregion.memstore.block.multiplier</name>

<value>2</value>

<description>

当一个region的memstore达到hbase.hregion.block.memstore*hbase.hregion.flush.size的指定倍数时,阻塞写请求。

这是一个避免在写请求高峰时期避免memstore耗尽的有效设置。如果没有上限限制,memstore被填满后发生flush时,

会消耗大量的时间来处理合并和分割,甚至导致OOM。

</description>

</property>

阻塞一个RS上的写请求会导致严重的结果,比你想象得到的更严重。由于hbase设计为一个region仅有一个RS提供,而写负载被均衡的分摊到整个集群(跨region),一个“低速”的RS会降低整个集群的性能。

注意:留意memstore的大小和memstoreflush队列的大小。memstore大小理论上不能超多upperLimit限制,而且不要让memstoreflush队列持续增长。

FrequentlyMemstoreFlushes

既然我们想避免写请求block的发生,那么尽早的触发flush,而不是等待达到产生写阻塞flush的条件不失为一种好的办法。但是,过于频繁的flush也会带来一些读性能的损失,以及集群的额外负载。

每次,每个memstoreflush会为每个CF生成一个storefile,频繁的flush会产生成千上万的storefile,那么每次读请求都需要寻找很多的storefile,读效率就可想而知了。

storefile合并操作是为了避免打开过多的storefile以致读效率恶化。Hbase会周期性的(或者达到配置的临界值)将较小的storefile合并成一个较大的。显然,flush的时候产生的storefile越多,那么所需的额外工作量和开销也就越多,更甚者,通常合并操作与请求操作并行,hbase如果跟不上合并的速度,同样也会blockRS上的写请求。鉴于上面的结果,这是我们非常不愿意见到的情况。

注意:留意RS上的合并任务队列。某些时候队列持续增长的话你需要多加注意,以免造成问题。

MoreonHFilescreation&Compactioncanbefoundhere.

理论上,memstore在不超过upperLimit的条件下应该尽可能的使用内存。So,ideallyMemstoreshoulduseasmuchmemoryasitcan(asconfigured,notallRSheap:therearealsoin-memorycaches),butnotcrosstheupperlimit.

MultipleColumnFamilies&MemstoreFlush

所有CF的memstore会一起flush,这就意味着每次flush会产生N个storefile,每个CF产生一个。因此,不平衡的CF数据大小会导致产生过多storefile:当memstore中的某一个CF数据量达到阈值,会导致其他所有memstore里面的其他CF也发生flush。过于频繁的flush和过多的storefile会影响集群的性能。

注意:大多数情况下,一个表只有一个CF是比较良好的设计方案。

HLog(WAL)Size&MemstoreFlush

你可能已经留意到,RS上有一个Write-aheadLog(以下简称WAL),数据会持续的默认写到这个文件中。它包含了所有已经提交到RS的,已经保留在memstore中但是尚未flush到storefile的数据编辑历史。这些在memstore中的,尚未持久化的数据,在RS发生错误时我们需要借助它进行恢复,它能重现这些尚未持久化的数据的编辑轨迹。.

当WAL(Hlog)变得非常大时,重现它的记录会花费非常多的时间。因此,我们需要限制WAL的大小,当达到这个大小时,会触发memstore的flush。当数据flush到磁盘后,WAL的大小就会变小,因为我们不需要再保存这些已经完成持久化的数据编辑记录。有两个属性用于配置这个条件:hbase.regionserver.hlog.blocksize和hbase.regionserver.maxlogs.可能你已经发现了,WAL的最大容量由hbase.regionserver.maxlogs*hbase.regionserver.hlog.blocksize(默认2GBbydefault)决定.当达到这个容量的时候,memstoreflush就会被触发。因此,你在调整memstore的的设置的时候,你同样需要调整这些值来适应那些变化。否则,WAL容量达到限制可能是导致memstoreflush的罪魁祸首,你专门配置memstore参数所增加的资源可能永远无法被物尽其用。除此以外,用WAL的容量来决定是否触发memstoreflush并不是一种最好的方式,因为一次性大批量flush多个region数据,会给你带来“风暴”一般的感受。

注意:确保hbase.regionserver.hlog.blocksize*hbase.regionserver.maxlogs比hbase.regionserver.global.memstore.lowerLimit*HBASE_HEAPSIZE的值只高那么一点点。.

Compression&MemstoreFlush

hbase建议将存储在HDFS上的文件进行压缩,可以节省可观的磁盘存储和网络传输成本。但是当数据flush到storefile的时候,Compression压缩并不会大幅度降低flush的效率,否则我们遇到上述的很多问题。DataiscompressedwhenitiswrittentoHDFS,i.e.whenMemstoreflushes.Compressionshouldnotslowdownflushingprocessalot,otherwisewemayhitmanyoftheproblemsabove,likeblockingwritescausedbyMemstorebeingtoobig(hitupperlimit)andsuch.

注意:当选择压缩的方式时,应当更关心压缩速度而不是压缩率。SNAPPY是一种不错的选择。

相关推荐