MemStore中的数据落盘后形成HFile文件,写入HDFS,HFile经历了3个版本,V1、V2、 V3,以下主要讲解V2版本
V2版本:
-
Scanned Block:
- Data Block
- Leaf index block / bloom block
-
Non-Scanned Block:
- Meta block
- Intermediate Level Data Index Blocks(optional)
-
Load-on-open:
- Root Data Index
- Fields for midkey
- File Info
- Bloom filter metadata(interpreted storefile)
-
Trailer:
- Trailer fields
- Version
Scanned Block
顺序扫描HFile时,所有的数据块将会被读取,包括Leaf Index block和 Bloom block
Non-Scanned Block:
HFile顺序扫描的时候数据不会被读取,包括Meta Block和Intermediate Level Data Index Blocks 两部分
Load-on-open:
这部分数据在HBase的RegionServer启动时,需要加载到内存,包括:
- FileInfo
- Bloom Filter block
- Data block index
- Meta block index
Trailer:
记录了HFile的基本信息,各个部分的偏移值和寻址信息
HFile由不同的Block组成,类型不同,但数据结构相同。
block大小可以再创建表指定,默认64k,大号block利于大规模顺序扫描,小号block有利于随机查找。
HBase将block统一抽象成HFileBlock,HFileBlock支持两种类型。一种含有checksum,另一种不含checksum。 选用不含有checksum的HFileBlock.
HFileBlock 包含两部分: BlockHeader: BlockType OnDiskSize UncompressedSize PreBlockOffset BlockData:
基本信息 Trailer:
RegionServer在打开HFile时会加载所有HFile的Trailer部分以及Load-on-openn部分到内存。实际加载过程会首先解析Trailer Block, 再进一步加载Load-on-open部分的数据。 1 加载HFile version版本信息,version包含major version, minor version两部分,major version决定了HFile的主版本,V1 V2 还是 V3 minor version再确定主版本的基础上决定是否支持一些微小修正,比如是否支持checksum,不同版本使用不同的解析器读取HFile.
Trailer block 可以计算出 load-on-open 在整个hfile文件中的偏移量和大小。load-on-open加载到内存,主要记录了一些元数据信息。
Data Block:
HBase中文件读取的最小单元,主要存KeyValue数据,KeyValue是HBase存储的核心。所有数据都以KeyValue方式存储到HBase中。 KeyValue组成部分:
KeyValue
KeyValue
Data Block: KeyValue
KeyValue
KeyLength
ValueLength
KeyValue: Key
Value
Key 由多部份组成, rowkey,column family,column qualfier,timestamp,keyType Value:用户写入的实际数据
keyType有如下几种: put delete deleteColumn DeleteFamily
底层kv存储,所有任意的keyValue中都包含rowkey,column family,column qualfier。比直接存储value更占用空间, 这也是再设计表结构时rowkey,column family,column qualfier尽可能短的根本原因。
Bloom block
布隆过滤器数据存储在内存中,所以该部分key的检索耗时基本可以忽略。不存在的一定不存在,对于存在的可能存在,需要进一步确认。
HFile文件越大,bloom中的位数组就会越大,一旦位数组太大,就不适合直接加载到内存了。 V2版本对位数组做了拆分。拆成多个独立的位数组。 根据key进行拆分,一部分连续的key使用一个位数组。一个HFile中就会包含多个位数组。根据key查找时,首先会定位到具体的位数组,只需要加载此位数组到 内存进行过滤即可,降低内存开销。
每个位数组对应HFile中的一个bloom block,因此多个位数组实际上会对应多个bloom block,为了方便根据key定位对应的位数组,V2设计相应的索引 bloom block index
bloom index block 是 bloom block的索引
bloom index block中bloom index entry指向bloom block,bloom block中实际存储了对应的位数组。
hfile中仅有一个bloom index block数据块,位于load-on-open部分。
bloom index block由两部分组成,元数据信息和执行bloomblock的索引信息。
1、根据待查找的key在bloom index block 所有的索引项中根据blockkey进行二分查找,定位到对应的bloom index entry
2、再根据bloom index entry 中的 blockoffset和blockondisksize加载该key对应的位数组 3、对key进行hash映射,都为1则说明可能存在。
生成HFile文件
执行Flush后生成对应HFile文件 首先构建Scanned Block,即KV写进来之后构建Data Block并依次写入文件,在形成Data Block的过程中也会依次构建形成 Leaf index block / bloom block并依次写入文件。直到memstore中所有的kv都写入完成。
Non-Scanned\Load-on-open\Trailer 这三部分是在所有kv数据完成写入后再追加写入的。
HFile中的bloom block 和 bloom block index
bloom block:存储实际的bloom数据
因为HFile中有多个DataBlock,每个DataBlock有多个bloom block,为什么会有多个bloom block?因为bloom block要加载到内存, 数据越大,bloom block也会越来越大,不适合全部加载到内存。所以会拆分成多个bloom block,检索时如何快速定位到具体的bloom block? 这就是bloom index的作用。
Bloom index 结构:
- BlockOffset
- BlockOnDiskSize
- BlockKeyLen
- BlockKey
BlockKey 其实是 Bloom block中的第一个原始KV的RowKey,给定一个待检索的RowKey,很容易通过 Bloom index定位到具体的bloom block,将该bloom block将在内存进行过滤,热点bloom block常驻内存。
假设落盘的数据比blockCache的新, 那么下一次get命中blockCache, 还会再去扫描hfile再合并吗? 如果会那么blockCache意义何在? 如果不会那么是不是会有脏数据?只要用户需要最新版本,rs一定会搜索所有可能存在这条数据的memstore和hfiles并做一次多路归并。在搜索hfile时可能对应的一些blocks已经在blockcache中,此时免去io操作。也就是说blockcache无法避免每次的多路扫描归并,只能减免过程中的一些io操作
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。