HBase – Memstore Flush、StoreFile、File解析

Memstore

Memstore 概述web

Memstore是HBase框架中很是重要的组成部分之一,是HBase可以实现高性能随机读写相当重要的一环。深刻理解Memstore的工做原理、运行机制以及相关配置,对hbase集群管理、性能调优都有着很是重要的帮助shell

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的个数网络

  1. Memstore级别限制:当Region中任意一个MemStore的大小达到了上限(hbase.hregion.memstore.flush.size,默认128MB),会触发Memstore刷新
  2. Region级别限制:当Region中全部Memstore的大小总和达到了上限(hbase.hregion.memstore.block.multiplier * hbase.hregion.memstore.flush.size,默认 2* 128M = 256M),会触发memstore刷新
  3. 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内存使用量)
  4. 当一个Region Server中HLog数量达到上限(可经过参数hbase.regionserver.maxlogs配置)时,系统会选取最先的一个 HLog对应的一个或多个Region进行flush
  5. HBase按期刷新Memstore:默认周期为1小时,确保Memstore不会长时间没有持久化。为避免全部的MemStore在同一时间都进行flush致使的问题,按期的flush操做有20000左右的随机延时
  6. 手动执行flush:用户能够经过shell命令 flush ‘tablename’或者flush ‘region name’分别对一个表或者一个Region进行flush

Memstore Flush流程负载均衡

为了减小flush过程对读写的影响,HBase采用了相似于两阶段提交的方式,将整个flush过程分为三个阶段:框架

  1. prepare阶段:遍历当前Region中的全部Memstore,将Memstore中当前数据集kvset作一个快照snapshot,而后再新建一个新的kvset。后期的全部写入操做都会写入新的kvset中,而整个flush阶段读操做会首先分别遍历kvset和snapshot,若是查找不到再会到HFile中查找。prepare阶段须要加一把updateLock对写请求阻塞,结束以后会释放该锁。由于此阶段没有任何费时操做,所以持锁时间很短。
  2. flush阶段:遍历全部Memstore,将prepare阶段生成的snapshot持久化为临时文件,临时文件会统一放到目录.tmp下。这个过程由于涉及到磁盘IO操做,所以相对比较耗时。
  3. commit阶段:遍历全部的Memstore,将flush阶段生成的临时文件移到指定的ColumnFamily目录下,针对HFile生成对应的storefile和Reader,把storefile添加到HStore的storefiles列表中,最后再清空prepare阶段生成的snapshot

Memstore 总结

Memstore 中的数据是排序的,当MemStore累计到必定阈值时
就会建立一个新的MemStore,而且将老的MemStore 添加到flush队列
flush时会将内存中不在版本范围内的数据所有删掉,而后再持久化到造成一个storeFile文件
而后后续的数据会写入一个新的storefile文件

StoreFile

StoreFile简介svg

1) 一个Region由多个Store组成,一个Store对应一个ColumnFamily(列族)
Store包括位于内存中的MemStore和位于磁盘的StoreFile;写操做先写入MemStore,当Memstore中的数据达到某个阈值,HRegionserver会启动flashcache进程写入StoreFile,每次写入造成单独的一个StoreFile
2)当StoreFile文件的数量增加到必定阈值后,系统会进行合并(minor、major compaction),在合并过程当中会进行版本合并和删除工做(majar),造成更大的StoreFile
3)当一个Region全部StoreFile的大小和数量超过必定阈值后,会把当前的Region分割为两个,并由HMaster分配到相应的HRegionserver服务器,实现负载均衡
4)客户端检索数据,先在MemStore找,找不到再找StoreFile

StoreFile以HFile格式保存在HDFS上
在这里插入图片描述

1)首先HFile文件是不定长的,长度固定的只有其中的两块:Trailer和FileInfo。正如图中所示的,Trailer中有指针指向其余数据块的起始点
2)File Info中记录了文件的一些Meta信息,例如:AVG_KEY_LEN, AVG_VALUE_LEN, LAST_KEY, COMPARATOR, MAX_SEQ_ID_KEY等
3)Data Index和Meta Index块记录了每一个Data块和Meta块的起始点
4)Data Block是HBase I/O的基本单元,为了提升效率,HRegionServer中有基于LRU的Block Cache机制。每一个Data块的大小能够在建立一个Table的时候经过参数指定,大号的Block有利于顺序Scan,小号Block利于随机查询。 每一个Data块除了开头的Magic之外就是一个个KeyValue对拼接而成, Magic内容就是一些随机数字,目的是防止数据损坏
5)HFile里面的每一个KeyValue对就是一个简单的byte数组。可是这个byte数组里面包含了不少项,而且有固定的结构。咱们来看看里面的具体结构:
在这里插入图片描述
开始是两个固定长度的数值,分别表示Key的长度和Value的长度。紧接着是Key,开始是固定长度的数值,表示RowKey的长度,紧接着是 RowKey,而后是固定长度的数值,表示Family的长度,而后是Family,接着是Qualifier(限定符),而后是两个固定长度的数值,表示Time Stamp和Key Type(Put/Delete)。Value部分没有这么复杂的结构,就是纯粹的二进制数据了

  • HFILE结构

解释1(较为清晰)

  • ① Data Block 段–保存表中的数据,这部分能够被压缩
  • ② Meta Block 段 (可选的)–保存用户自定义的kv对,能够被压缩
  • ③ File Info 段–Hfile的元信息,不被压缩,用户也能够在这一部分添加本身的元信息
  • ④ Data Block Index 段–Data Block的索引。每条索引的key是被索引的block的第一条记录的key
  • ⑤ Meta Block Index段 (可选的)–Meta Block的索引
  • ⑥ Trailer–这一段是定长的。保存了每一段的偏移量,读取一个HFile时,会首先 读取Trailer,Trailer保存了每一个段的起始位置(段的Magic Number用来作安全check),而后,DataBlock Index会被读取到内存中,这样,当检索某个key时,不须要扫描整个HFile,而只需从内存中找到key所在的block,经过一次磁盘io将整个 block读取到内存中,再找到须要的key。DataBlock Index采用LRU机制淘汰
  • ⑦ HFile的Data Block,Meta Block一般采用压缩方式存储,压缩以后能够大大减小网络IO和磁盘IO,随之而来的开销固然是须要花费cpu进行压缩和解压缩。
    目标Hfile的压缩支持两种方式:Gzip,Lzo

解释2(较为简洁明了)

  • ① Data(数据块):保存表中的数据(KeyValue的形式),这部分能够被压缩。
  • ② Meta (元数据块):存储用户自定义KeyValue
  • ③ File Info:定长;记录了文件的一些元信息,例如:AVG_KEY_LEN,AVG_VALUE_LEN, LAST_KEY, COMPARATOR, MAX_SEQ_ID_KEY等
  • ④ Data Index(数据块索引):记录了每一个Data块的起始索引
  • ⑤ Meta Index(元数据块索引):记录了每一个Meta块的起始索引
  • ⑥ Trailer:定长;用于指向其余数据块的起始点。

StoreFile 总结

当一个Store中的StoreFile达到必定的阈值后,就会 进行一次合并(major compact),将对同一个key的修改合并到一块儿 造成一个大的StoreFile,当StoreFile大小达到必定的阈值后,又会对 StoreFile进行Split,等分红两个StoreFile