Memstore是HBase框架中非常重要的组成部分之一,是HBase能够实现高性能随机读写至关重要的一环。深入理解Memstore的工作原理、运行机制以及相关配置,对hbase集群管理、性能调优都有着非常重要的帮助。
Memstore 概述
HBase中,Region是集群节点上最小的数据服务单元,用户数据表由一个或多个Region组成。在Region中每个ColumnFamily的数据组成一个Store。每个Store由一个Memstore和多个HFile组成,如下图所示:
之前我们提到,HBase是基于LSM-Tree模型的,所有的数据更新插入操作都首先写入Memstore中(同时会顺序写到日志HLog中),达到指定大小之后再将这些修改操作批量写入磁盘,生成一个新的HFile文件,这种设计可以极大地提升HBase的写入性能;另外,HBase为了方便按照RowKey进行检索,要求HFile中数据都按照RowKey进行排序,Memstore数据在flush为HFile之前会进行一次排序,将数据有序化;还有,根据局部性原理,新写入的数据会更大概率被读取,因此HBase在读取数据的时候首先检查请求的数据是否在Memstore,写缓存未命中的话再到读缓存中查找,读缓存还未命中才会到HFile文件中查找,最终返回merged的一个结果给用户。
可见,Memstore无论是对HBase的写入性能还是读取性能都至关重要。其中flush操作又是Memstore最核心的操作,接下来重点针对Memstore的flush操作进行深入地解析:首先分析HBase在哪些场景下会触发flush,然后结合源代码分析整个flush的操作流程,最后再重点整理总结和flush相关的配置参数,这些参数对于性能调优、问题定位都非常重要。
Memstore Flush触发条件
HBase会在如下几种情况下触发flush操作,需要注意的是MemStore的最小flush单元是HRegion而不是单个MemStore。可想而知,如果一个HRegion中Memstore过多,每次flush的开销必然会很大,因此我们也建议在进行表设计的时候尽量减少ColumnFamily的个数。
- Memstore级别限制:当Region中任意一个MemStore的大小达到了上限(hbase.hregion.memstore.flush.size,默认128MB),会触发Memstore刷新。
- Region级别限制:当Region中所有Memstore的大小总和达到了上限(hbase.hregion.memstore.block.multiplier * hbase.hregion.memstore.flush.size,默认 2* 128M = 256M),会触发memstore刷新。
- Region Server级别限制:当一个Region Server中所有Memstore的大小总和达到了上限(hbase.regionserver.global.memstore.upperLimit * hbase_heapsize,默认 40%的JVM内存使用量),会触发部分Memstore刷新。Flush顺序是按照Memstore由大到小执行,先Flush Memstore最大的Region,再执行次大的,直至总体Memstore内存使用量低于阈值(hbase.regionserver.global.memstore.lowerLimit * hbase_heapsize,默认 38%的JVM内存使用量)。
- 当一个Region Server中HLog数量达到上限(可通过参数hbase.regionserver.maxlogs配置)时,系统会选取最早的一个 HLog对应的一个或多个Region进行flush
- HBase定期刷新Memstore:默认周期为1小时,确保Memstore不会长时间没有持久化。为避免所有的MemStore在同一时间都进行flush导致的问题,定期的flush操作有20000左右的随机延时。
- 手动执行flush:用户可以通过shell命令 flush ‘tablename’或者flush ‘region name’分别对一个表或者一个Region进行flush。
Memstore Flush流程
为了减少flush过程对读写的影响,HBase采用了类似于两阶段提交的方式,将整个flush过程分为三个阶段:
- prepare阶段:遍历当前Region中的所有Memstore,将Memstore中当前数据集kvset做一个快照snapshot,然后再新建一个新的kvset。后期的所有写入操作都会写入新的kvset中,而整个flush阶段读操作会首先分别遍历kvset和snapshot,如果查找不到再会到HFile中查找。prepare阶段需要加一把updateLock对写请求阻塞,结束之后会释放该锁。因为此阶段没有任何费时操作,因此持锁时间很短。
- flush阶段:遍历所有Memstore,将prepare阶段生成的snapshot持久化为临时文件,临时文件会统一放到目录.tmp下。这个过程因为涉及到磁盘IO操作,因此相对比较耗时。
- commit阶段:遍历所有的Memstore,将flush阶段生成的临时文件移到指定的ColumnFamily目录下,针对HFile生成对应的storefile和Reader,把storefile添加到HStore的storefiles列表中,最后再清空prepare阶段生成的snapshot。
上述flush流程可以通过日志信息查看:
/******* prepare阶段 ********/ 2016-02-04 03:32:41,516 INFO [MemStoreFlusher.1] regionserver.HRegion: Started memstore flush for sentry_sgroup1_data,{\xD4\x00\x00\x01|\x00\x00\x03\x82\x00\x00\x00?\x06\xDA`\x13\xCAE\xD3C\xA3:_1\xD6\x99:\x88\x7F\xAA_\xD6[L\xF0\x92\xA6\xFB^\xC7\xA4\xC7\xD7\x8Fv\xCAT\xD2\xAF,1452217805884.572ddf0e8cf0b11aee2273a95bd07879., current region memstore size 128.9 M /******* flush阶段 ********/ 2016-02-04 03:32:42,423 INFO [MemStoreFlusher.1] regionserver.DefaultStoreFlusher: Flushed, sequenceid=1726212642, memsize=128.9 M, hasBloomFilter=true, into tmp file hdfs://hbase1/hbase/data/default/sentry_sgroup1_data/572ddf0e8cf0b11aee2273a95bd07879/.tmp/021a430940244993a9450dccdfdcb91d /******* commit阶段 ********/ 2016-02-04 03:32:42,464 INFO [MemStoreFlusher.1] regionserver.HStore: Added hdfs://hbase1/hbase/data/default/sentry_sgroup1_data/572ddf0e8cf0b11aee2273a95bd07879/d/021a430940244993a9450dccdfdcb91d, entries=643656, sequenceid=1726212642, filesize=7.1 M
整个flush过程可能涉及到compact操作和split操作,因为过于复杂,在此暂时略过不表。
Memstore Flush对业务读写的影响
上文介绍了HBase在什么场景下会触发flush操作以及flush操作的基本流程,想必对于HBase用户来说,最关心的是flush行为会对读写请求造成哪些影响以及如何避免。因为不同触发方式下的flush操作对用户请求影响不尽相同,因此下面会根据flush的不同触发方式分别进行总结,并且会根据影响大小进行归类:
影响甚微
正常情况下,大部分Memstore Flush操作都不会对业务读写产生太大影响,比如这几种场景:HBase定期刷新Memstore、手动执行flush操作、触发Memstore级别限制、触发HLog数量限制以及触发Region级别限制等,这几种场景只会阻塞对应Region上的写请求,阻塞时间很短,毫秒级别。
影响较大
然而一旦触发Region Server级别限制导致flush,就会对用户请求产生较大的影响。会阻塞所有落在该Region Server上的更新操作,阻塞时间很长,甚至可以达到分钟级别。一般情况下Region Server级别限制很难触发,但在一些极端情况下也不排除有触发的可能,下面分析一种可能触发这种flush操作的场景:
相关JVM配置以及HBase配置:
maxHeap = 71 hbase.regionserver.global.memstore.upperLimit = 0.35 hbase.regionserver.global.memstore.lowerLimit = 0.30
基于上述配置,可以得到触发Region Server级别的总Memstore内存和为24.9G,如下所示:
2015-10-12 13:05:16,232 INFO [regionserver60020] regionserver.MemStoreFlusher: globalMemStoreLimit=24.9 G, globalMemStoreLimitLowMark=21.3 G, maxHeap=71 G
假设每个Memstore大小为默认128M,在上述配置下如果每个Region有两个Memstore,整个Region Server上运行了100个region,根据计算可得总消耗内存 = 128M * 100 * 2 = 25.6G > 24.9G,很显然,这种情况下就会触发Region Server级别限制,对用户影响相当大。
根据上面的分析,导致触发Region Server级别限制的因素主要有一个Region Server上运行的Region总数,一个是Region上的Store数(即表的ColumnFamily数)。对于前者,根据读写请求量一般建议线上一个Region Server上运行的Region保持在50~80个左右,太小的话会浪费资源,太大的话有可能触发其他异常;对于后者,建议ColumnFamily越少越好,如果从逻辑上确实需要多个ColumnFamily,最好控制在3个以内。
总结
本文主要介绍了HBase引擎中至关重要的一个组件-Memstore,主要介绍了Memstore Flush的几种触发条件、Flush完整流程以及各种不同场景下Flush对业务读写的影响。希望通过此篇文章可以对Memstore有一个更深入的了解。
博主的文章很靠谱,正在按照时间顺序看一遍。有一个问题,“还有,根据局部性原理,新写入的数据会更大概率被读取,因此HBase在读取数据的时候首先检查请求的数据是否在Memstore,写缓存未命中的话再到读缓存中查找,读缓存还未命中才会到HFile文件中查找,最终返回merged的一个结果给用户。”
我在StoreScanner里面找到了if (scan instanceof InternalScan) 的判断,但是如果是InternalScan而且mem only,那么只会在memstore里面找,否则就会包含HFile。请问这个“memstore没有->读缓存”的递进关系在哪?
HBase读取这块逻辑有点复杂 这里涉及到内部scanner的实现 StoreScanner由StoreFileScanner和MemStoreScanner组成 hbase首先会在memstorescanner中查找 如果没有找到 才会雇佣storefilescanner查找 storefilescanner封装了查找读缓存和hfile的逻辑 首先在blockcache中国年查找 没找到再到hfile中查找
希望有所帮助
感谢回复。
我表达的意思就是在scanner实现里面。比如get操作的在server端的逻辑,是hregion.get –> new RegionScannerImpl() –> store.getScanner –> new StoreScanner –> scanners = getScannersNoCompaction(); resetKVHeap(scanners); 然后实际用这个heap去进行真正的查找。而getScannersNoCompaction调用selectScannersFrom(hstore.getScanners())。hstore.getScanners()将memstore/hfile的scanner不加区分地封装在一起,selectScannersFrom根据ts或者bloom过滤掉不可能的scanner,没有对先到memstorescanner查找的印象。
从代码上看不出来先到memstorescanner查找的 这个需要考虑storescanner的排序 排序规则是按照时间戳进行的 自然memstorescanner会排在前面
可能是这个理解不一样,我理解是,建立heap的过程中已经seek了HFile,就已经发生了对HFile的读取(部分,至少读了load-on-open和索引部分,以及对应的block)。
除此之外,先查cache再查HFile的逻辑应该是错的。在关闭cache on write的情况下,一开始a=0,写到了hfile;读取a的值,然后cache住对应block;写a=1;flush对应memstore;再次读取a的时候,此时memstore里面没有a,cache里面有a=0,这时候直接返回a=0显然是错误的。
先查cache再查hfile的逻辑是没问题的,可以参考HFileReaderV2文件中的readBlock方法。至于你说的这个问题,后续更新a的操作会flush成一个新的文件,并不是修改的blockcache中那个a。假设当前a这个数据更新了一次,有两个版本:v1和v2,按照你的说法应该是v1版本的a在blockcache中(对应的文件是hfile1) ,v2版本的a在hfile2中,如果查询最新版本的a,只会查询hfile2的a;如果查询所有版本,首先会在cache中查,查不到再到hfile2中查,查到v2版本。接着会查v1版本的,直接在cache中就可以找到。
希望有所帮助
大概理解了你的意思,还是对“读取”的理解不一样。谢谢了!
你好,不知道我理解对不对?
一个查询需要查一个Store中的mem和file。
如果发现block在blockcache中会去blockcache中读取,就可以不需要去读取那个file。
mem是肯定要被读取(否则读取的数据有问题)。
cache和file两者不一定,但是新生成的file如果没有被读取过肯定不在blockcache中。
嗯 理解基本正确
对于另一篇文章中的问题,确实感觉blockcache最大的隐患来自于文件的更改(包括合并,flush),他会导致blockcache的缓存不一致,这个问题的关键是不是应该在于,文件的更改能否让StoreFileScanner的可见性达到需要的程度。ps:一个刚接触hbase一周的人,还没开始看源码,如果说错或问错,请不要喷小白。
blockcache缓存不存在不一致的问题 memstore更新到hfile的数据是以append形式追加写的(形成多版本) 不是update原有数据 这点对于理解blockcache的那个问题至关重要
扫一眼Hstore,感觉ReentrantReadWriteLock org.apache.hadoop.hbase.regionserver.HStore.lock 这个锁的控制很关键,还没非常细看。这个锁的控制应该能解释另外那篇文章的问题。
感觉对这个问题的理解方向有点不一致。评论貌似只能到的五层。==!
hbase.regionserver.global.memstore.upperLimit * hbase_heapsize,默认 40%的JVM内存使用量),会触发部分Memstore刷新。Flush顺序是按照Memstore由大到小执行,先Flush Memstore最大的Region,再执行次大的,直至总体Memstore内存使用量低于阈值(hbase.regionserver.global.memstore.lowerLimit * hbase_heapsize,默认 38%的JVM内存使用量)这个是不是有问题?
这部分低于阈值hbase.regionserver.global.memstore.lowerLimit * hbase_heapsize是有问题的吧,这个阈值应该是hbase.regionserver.global.memstore.lowerLimit * globalMemStoreLimit(hbase.regionserver.global.memstore.upperLimit * hbase_heapsize)吧
再次确认了下,阈值是hbase.regionserver.global.memstore.lowerLimit * hbase_heapsize,不清楚hbase.regionserver.global.memstore.lowerLimit * globalMemStoreLimit是怎么来的?
如果总是isAboveLowWaterMark,那么会一直进行flush吧,而其中的引用到的globalMemStoreLimitLowMark的赋值如下:
this.globalMemStoreLimitLowMark =(long) (this.globalMemStoreLimit * this.globalMemStoreLimitLowMarkPercent); 具体可以参看MemStoreFlusher的构造函数。难道是我理解的有问题?不知道你是从哪看到flush的条件的?
HBase看的什么版本的?我看的是0.98版本,你的呢?
1.1.3
交流群号是啥呢?
很多人用qq有道理啊 可以有交流群号 微信只有二维码 anyway 二维码发你邮箱你361197893@qq.com
请问可以加入Hbase交流群吗?
欢迎,二维码已经发送你的邮箱,请查收
申请加入HBase交流群,谢谢
可以先加我微信 libisthanks 我拉进来
hbase微信交流群二维码能发我一个么?
谢谢!
可以先加我微信哈
同问可以加入交流群嘛。。
微信群二维码会经常过期 可以先加我微信libisthanks 我邀请加群~
求加群交流
先加我微信 libisthanks
flush是Region级别的。而一个region含有多个store的。为何各个store自己独立flush呢。
官方也推荐一个region不要有太多的column family,因为flush的时候会导致大量相邻的column family一起flush的,
造成大量的IO操作。
那么请教下,是否可以每个store独立flush呢。
以region做store的考虑点是什么呢。毕竟在sequenceId中,也是每个store自己维护自己的id的。
0.98版本的HBase中sequenceId还是region级别的,所以flush就是region级别的。记得再1.1版本中把sequenceId做成store级别的,这样就可以实现store级别的flush,1.1以上版本flush默认策略是FlushLargeStoresPolicy
因此HBase在读取数据的时候首先检查请求的数据是否在Memstore,写缓存未命中的话再到读缓存中查找,读缓存还未命中才会到HFile文件中查找,最终返回merged的一个结果给用户————请问如果一个rowkey的数据部分在Memstore,部分数据在hdfs中,这种情况岂不是之返回Memstore的数据了?
部分是什么意思?一行中某些列?
楼主留个群号呗
可以加我微信:libisthanks,我拉进群
在memstore flush 机制中
4、当一个Region Server中HLog数量达到上限(可通过参数hbase.regionserver.maxlogs配置)时,系统会选取最早的一个 HLog对应的一个或多个Region进行flush。
“一个Region Server中HLog数量” 不就只有一个嘛,为什么一个region server 的hlog数量之说,而且博主在“HBase - 数据写入流程解析”里面也说了“每个Region Server拥有一个HLog日志”。这边描述是否有点问题。不是很明白,还是博主解答。
《HBase - 数据写入流程解析》中说到“每个Region Server拥有一个HLog日志”是想强调所有Region共用HLog。一个Region Server中所有Region都会向同一个HLog写日志,当HLog大小超过阈值就会新生成一个HLog文件接收新的写入。所以HLog文件个数实际上有多个的。
你好,首先为不错的分享博客点赞!请教下,当Hlog数量达到一定数量后会flush。 这个触发条件的目的是? 也就是说过多的Hlog,对memstore有什么影响、
你好,regionserver中出现这样的日志“INFO org.apache.hadoop.hbase.regionserver.HRegionServer: my-hbase-srv3,60020,1506017784241-MemstoreFlusherChore requesting flush of regionInfoKey,1499557430610.4ac8c97fadd652f676a803688d2ff07e. because cfInfo has an old edit so flush to free WALs after random delay 241441ms”,可以这么理解吗?由于写入量太大,导致flush不及时(代码中会批量手动flush),最后触发定时刷新?
because cfInfo has an old edit so flush to free WALs 这个是flush的最终原因。这个memstore长时间没有flush导致已经写入到memstore中的数据在WAL中太老,对应的WALEdits之后的日志都不能被释放,最终导致系统WAL日志数据太大。所以需要赶紧将这个memstore flush掉,对应的WAL Edit就可以清理掉了,这个Edit清理掉之后,之后的Edit也就可以清理掉了
对于写流程,可以这么理解吗?客户端设置writerbuffersize(假如6M),满了后会flush写入memstore和WAL,然后memstore满了后,会flush到HFile,对于memstore中残余的数据(最后的未满128M数据),就等待定时flush,此时出现日志信息“has an old edit so flush to free WALs”?
基本可以这么理解
出现大量的
because cfInfo has an old edit so flush to free WALs after random delay
如何解决。
刚刚接触hbase
想请问范老师
hbase flush的hfile是如何存入hdfs的
是否与hadoop hdfs上传文件的命令机制是一样的
我仅仅了解到 hdfs上传文件的命令机制 从而知道了相关block的具体物理存储位置
那么hfile存入hdfs的方式是否有所变化?还是与其相同
调用HDFS的客户端通过流的方式写到HDFS的
因此HBase在读取数据的时候首先检查请求的数据是否在Memstore,写缓存未命中的话再到读缓存中查找,读缓存还未命中才会到HFile文件中查找,
———————————————
你好!这里我有个疑问,假如我是scan查询,写缓存或者读缓存只有部分数据满足要求,这时服务端是直接返回这部分数据,还是会继续到HFile中查找
全部查询得到所有结果之后统一返回
版本hbase 2.0 , 在写入数据是,Memstore 在小于10m 就开始flush,查看资源并没有达到region级别、regionserver 级别的资源上线
flush后的文件大小小于10m还是memstore大小小于10m?
请问,同一个region是否可以包含不同表的数据(CF/store)?
不可以的 逻辑上讲表下面是region的概念
范欣欣:你好!
我想问一下 如果有刷新请求时候,做快照 快照会阻塞么。
做快照是一个很快的过程 会阻塞一下下 然后新开一个SkipList去接收新的数据写入
你好:
首先谢谢你的回复,我做了一个快照大约在10:34,在10:40还没有结束,我看日志有其他条件出发flush,不知道这个快照延迟是否因为这个原因:
master日志:一直爆这个错误,在这6分钟内
WARN org.apache.hadoop.hbase.master.ServerManager: RegionServer pc-zjqbba478.zj.chinamobile.com,60020,1550853919984 indicates a last flushed sequence id (325) that is less than the previous last flushed sequence id (696) for region
rs:我认为是他的flush,导致快照阻塞
MemstoreFlusherChore requesting flush of **,,1531369433217.12c1efb57752704024f27df1d79a0f63. because cpf has an old edit so flush to free WALs after random delay 121742ms
但是我看到hdfs:
10:38:11,263 INFO org.apache.hadoop.hdfs.server.datanode.DataNode: Likely the client has stopped reading, disconnecting it 192.168.6.201:dataXceiver error processing READ_BLOCK operation src: /192.168.6.201:42559 dst: /192.168.6.202:50010); java.net.SocketTimeoutException: 480000 millis timeout while waiting for channel to be ready for write. ch : java.nio.channels.SocketChannel[connected local=ip:50010 remote=:42559]
请问下 flush prepare阶段“然后再新建一个新的kvset。后期的所有写入操作都会写入新的kvset中”
这个kvset就是新的MemStore,而且是用的SkipList实现的吗?
在整个flush期间,这些写入到新kvset的数据应该是对读不可见的吧?那是什么时候才可见呢?
在最新的HBase官方文档中,没有下面这个Flush情况呢:
Region级别限制:当Region中所有Memstore的大小总和达到了上限(hbase.hregion.memstore.block.multiplier * hbase.hregion.memstore.flush.size,默认 2* 128M = 256M),会触发memstore刷新。
只是说了会当MemStore达到hbase.hregion.memstore.block.multiplier * hbase.hregion.memstore.flush.size,会阻塞更新操作:
hbase.hregion.memstore.block.multiplier
Description
Block updates if memstore has hbase.hregion.memstore.block.multiplier times hbase.hregion.memstore.flush.size bytes. Useful preventing runaway memstore during spikes in update traffic. Without an upper-bound, memstore fills such that when it flushes the resultant flush files take a long time to compact or split, or worse, we OOME.
Default
4
代码里面有requestFlush
博主您好,刚刚接触hbase不久,我想请教一个问题:集群某个region在进行flush时候,其状态频繁地长时间处于“Disabling compacts and flushes for region ”,并导致RIT,这一般什么情况下导致的,谢谢!hbase使用版本1.2.4
flush并不会引起rit,这个日志出现在region关闭的时候,应该是region关闭导致的
请问下,我们集群现在40台,给了196G内存,读和写各占0.4,也做了一些优化,但是读取大概20ms,这个算正常吗?
什么读?get还是scan,如果是get的话不太正常
对于 prepare 阶段的说明,想请教一下:
> 将Memstore中当前数据集kvset做一个快照snapshot,然后再新建一个新的kvset。后期的所有写入操作都会写入新的kvset中,而整个flush阶段读操作会首先分别遍历kvset和snapshot,如果查找不到再会到HFile中查找。prepare阶段需要加一把updateLock对写请求阻塞,结束之后会释放该锁。因为此阶段没有任何费时操作,因此持锁时间很短。
1,后期的所有写入操作都会写入新的kvset中
2,prepare阶段需要加一把updateLock对写请求阻塞
在 2 中对写请求阻塞,而 1 中的写入操作都会写入新的 kvset。个人理解可能不太对,感觉有点矛盾的感觉。