ImageVerifierCode 换一换
格式:DOCX , 页数:9 ,大小:126.51KB ,
资源ID:8134132      下载积分:10 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.docduoduo.com/d-8134132.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录   微博登录 

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(Hfile文件详解.docx)为本站会员(fmgc7290)主动上传,道客多多仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知道客多多(发送邮件至docduoduo@163.com或直接QQ联系客服),我们立即给予删除!

Hfile文件详解.docx

1、Table of ContentsHFile 存储格式Block 块结构HFile 存储格式HFile 是参照谷歌的 SSTable 存储格式进行设计的,所有的数据记录都是通过它来完成持久化,其内部主要采用分块的方式进行存储,如图所示:每个 HFile 内部包含多种不同类型的块结构,这些块结构从逻辑上来讲可归并为两类,分别用于数据存储和数据索引(简称数据块和索引块) ,其中数据块包括:(1) DATA_BLOCK:存储表格数据(2) BLOOM_CHUNK:存储布隆过滤器的位数组信息(3) META_BLOCK:存储元数据信息(4) FILE_INFO:存储 HFile 文件信息索引块包括:

2、表格数据索引块(ROOT_INDEX、INTERMEDIATE_INDEX、LEAF_INDEX)在早期的 HFile 版本中(version-1),表格数据是采用单层索引结构进行存储的,这样当数据量上升到一定规模时,索引数据便会消耗大量内存,导致的结果是Region 加载效率低下(A region is not considered opened until all of its block index data is loaded)。因此在 version-2 版本中,索引数据采用多层结构进行存储,加载 HFile 时只将根索引(ROOT_INDEX)数据载入内存,中间索引 (INTERM

3、EDIATE_INDEX)和叶子索引(LEAF_INDEX)在读取数据时按需加载,从而提高了 Region 的加载效率。 元数据索引块(META_INDEX)新版本的元数据索引依然是单层结构,通过它来获取元数据块信息。 布隆索引信息块(BLOOM_META)通过索引信息来遍历要检索的数据记录是通过哪一个 BLOOM_CHUNK 进行映射处理的。从存储的角度来看,这些数据块会划分到不同的区域进行存储。1. Trailer 区域该区域位于文件的最底部,HFile 主要通过它来实现相关数据的定位功能,因此需要最先加载,其数据内容是采用 protobuf 进行序列化处理的,protocol 声明如下:

4、message FileTrailerProto optional uint64 file_info_offset = 1; optional uint64 load_on_open_data_offset = 2; optional uint64 uncompressed_data_index_size = 3; optional uint64 total_uncompressed_bytes = 4; optional uint32 data_index_count = 5; optional uint32 meta_index_count = 6; optional uint64 ent

5、ry_count = 7; optional uint32 num_data_index_levels = 8; optional uint64 first_data_block_offset = 9; optional uint64 last_data_block_offset = 10; optional string comparator_class_name = 11; optional uint32 compression_codec = 12;optional bytes encryption_key = 13;FileInfo 数据块在 HFile 中的偏移量信息;Load-on

6、-open 区域在 HFile 中的偏移量信息;所有表格索引块在压缩前的总大小;所有表格数据块在压缩前的总大小;根索引块中包含的索引实体个数;元数据索引块中包含的索引实体个数;文件所包含的 KeyValue 总数;表格数据的索引层级数;第一个表格数据块在 HFile 中的偏移量信息;最后一个表格数据块在 HFile 中的偏移量信息;在代码层面上 Trailer 是通过 FixedFileTrailer 类来封装的,可通过其readFromStream 方法用来读取指定 HFile 的 Trailer 信息。2. Load-on-open 区域HFile 被加载之后,位于该区域中的数据将会被载入

7、内存,该区域的起始位置通过Trailer 来定位(通过其 load_on_open_data_offset 属性)。从该位置起依次保存的数据信息为:根索引快、元数据索引块、文件信息块以及布隆索引块。3. Scanned-Block 区域在执行 HFile 顺序扫描时,位于该区域中的所有块信息都需要被加载,包括:表格数据块、布隆数据块和叶子索引块(后两者称之为 InlineBlock)。4. Non-Scanned-Block 区域在执行 HFile 顺序扫描时,位于该区域中的存储块可不被加载,包括:元数据块和中间索引块。Block 块结构每个 Block 块是由 3 部分信息组成的,分别是:

8、header 信息、data 信息以及用于 data 校验的 checksum 信息。不同类型的 block 只是在 data 信息的存储结构上存在差异,而header 信息和 checksum 信息存储结构基本一致。1. header 主要用于存储每个 Block 块的元数据信息这些信息包括:(1)blockType:块类型,HFile 一共对外声明了 10 种不同类型的 Block,分别是:DATA(表格数据块)、META( 元数据块) 、BLOOM_CHUNK(布隆数据块)、FILE_INFO(文件信息块) 、TRAILER、LEAF_INDEX(叶子索引块)、INTERMEDIATE_

9、INDEX(中间索引块 )、ROOT_INDEX(根索引快) 、BLOOM_META(布隆索引块)、和 META_INDEX(元数据索引块);(2)onDiskSizeWithoutHeader:data 信息与 checksum 信息所占用的磁盘空间大小;(3)onDiskDataSizeWithHeader:data 信息与 header 信息所占用的磁盘空间大小;(4)uncompressedSizeWithoutHeader:每个 block 块在完成解压缩之后的大小(不包括 header 和 checksum 占用的空间 );(5)prevBlockOffset:距离上一个同类型 b

10、lock 块的存储偏移量大小。在 v2 版本中,header 的长度为固定的 33 字节。2. data 主要用于封装每个 block 块的核心数据内容 如果是根索引块其数据内容如下:主要包含多条索引实体信息(索引实体的个数记录在 Trailer 中)以及 midKey相关信息,其中每条索引实体信息是由 3 部分数据组成的,分别为:(1)Offset:索引指向的 Block 块在文件中的偏移量位置;(2)DataSize:索引指向的 Block 块所占用的磁盘空间大小( 在 HFile 中的长度);(3)Key:如果索引指向的是表格数据块(DATA_BLOCK),该值为目标数据块中第一条数据记

11、录的 rowkey 值(0.95 版本之前是这样的,之后的版本参考 HBASE-7845);如果索引指向的是其他索引块,该值为目标索引块中第一条索引实体的 blockKey 值。而 midKey 信息主要用于定位 HFile 的中间位置,以便于对该 HFile 执行split 拆分处理,其数据内容同样由 3 部分信息组成,分别为:(1)midLeafBlockOffset:midKey 所属叶子索引块在 HFile 中的偏移量位置;(2)midLeafBlockOnDiskSize:midKey 所属叶子索引块的大小(在 HFile 中的长度);(3)midKeyEntry:midKey 在其

12、所属索引块中的偏移量位置。 如果是非根索引块其数据内容如下:同样包含多条索引实体信息,但不包含 midKey 信息。除此之外还包含了索引实体的数量信息以及每条索引实体相对于首个索引实体的偏移量位置。 如果是表格数据块其数据内容为多条 KeyValue 记录,每条 KeyValue 的存储结构可参考 Memstore 组件实现章节。 如果是元数据索引块其数据内容同叶子索引块类似,只不过索引实体引向的是 META 数据块。 如果是布隆数据块其数据内容为布隆过滤器的位数组信息。 如果是布隆索引块其数据内容如下:同其他索引块类似,包含多条索引实体信息,每条索引实体引向布隆数据块(BLOOM_CHUNK

13、)。除此之外还包含与布隆过滤器相关的元数据信息,包括:(1)version:布隆过滤器版本,在新版本 HBase 中布隆过滤器通过CompoundBloomFilter 类来实现,其对应的版本号为 3;(2)totalByteSize:所有布隆数据块占用的磁盘空间总大小;(3)hashCount:元素映射过程中所使用的 hash 函数个数;(4)hashType:元素映射过程中所采用的 hash 函数类型(通过hbase.hash.type 属性进行声明);(5)totalKeyCount:所有布隆数据块中已映射的元素数量;(6)totalMaxKeys:在满足指定误报率的情况下(默认为百分之

14、一 ),所有布隆数据块能够映射的元素总量;(7)numChunks:目前已有布隆数据块的数量;(8)comparator:所映射元素的排序比较类,默认为org.apache.Hadoop.hbase.KeyValue.RawBytesComparator 如果是文件信息块其数据内容采用 protobuf 进行序列化,相关 protocol 声明如下: message FileInfoProto repeated BytesBytesPair map_entry = 1; / Map of name/values 3. checksum 信息用于校验 data 数据是否正确块信息读取数据块的读取

15、操作主要是通过 FSReader 类的 readBlockData 方法来实现的,在执行数据读取操作之前,需要首先知道目标数据块在 HFile 中的偏移量位置,有一些数据块的偏移量信息是可通过 Trailer 进行定位的,如: 根索引块(ROOT_INDEX) 的偏移量信息可通过 Trailer 的load_on_open_data_offset 属性来定位,在知道了根索引块的存储信息之后,便可通过它来定位所有 DATA_BLOCK 在 HFile 中的偏移量位置; 首个 DATA_BLOCK 的偏移量信息可通过 Trailer 的 first_data_block_offset 属性来定位。

16、在不知道目标数据块大小的情况下需要对 HFile 执行两次查询才能读取到最终想要的HFileBlock 数据。第一次查询主要是为了读取目标 Block 的 header 信息,由于 header 具有固定的长度(HFileV2 版本为 33 字节),因此在知道目标 Block 的偏移量之后,便可通过读取指定长度的数据来将 header 获取。获取到 header 之后便可通过其 onDiskSizeWithoutHeader 属性来得知目标数据块的总大小。totalSize = headerSize + onDiskSizeWithoutHeader然后再次从 Block 的偏移量处读取长度为

17、 totalSize 字节的数据,以此来构造完整的HFileBlock 实体。由以上逻辑来看,如果在读取数据块之前,能够事先知道该数据块的大小,那么便可省去header 的查询过程,从而有效降低 IO 次数。为此,HBase 采用的做法是在读取指定Block 数据的同时,将下一个 Block 的 header 也一并读取出来(通过读取 totalSize + headerSize 长度的数据 ),并通过 ThreadLocal 将该 header 进行缓存。这样如果当前线程所访问的数据是通过两个连续的 Block 进行存储的,那么针对第二个 Block 的访问只需执行一次 IO 即可。获取到

18、HFileBlock 实体之后,可通过其 getByteStream 方法来获取内部数据的输入流信息,在根据不同的块类型来选择相应的 API 进行信息读取:(1)如果 block 为根索引块,其信息内容可通过 BlockIndexReader 进行读取,通过其readMultiLevelIndexRoot 方法;(2)如果为元数据索引块,同样采用 BlockIndexReader 进行读取,通过其 readRootIndex方法;(3)如果为非根索引块,可通过 BlockIndexReader 的 locateNonRootIndexEntry 方法来将数据指针定位到目标 block 的索引位

19、置上,从而对目标 block 的偏移量、大小进行读取;(4)如果为文件信息块,通过 FileInfo 类的 read 方法进行读取;(5)如果为布隆索引块,通过 HFile.Reader 实体的 getGeneralBloomFilterMetadata 方法进行读取;(6)如果为布隆数据块,通过该 HFileBlock 实体的 getBufferWithoutHeader 方法来获取布隆数据块的位数组信息(参考 CompoundBloomFilter 类的实现 )。块数据生成Block 数据在写入 HFile 之前是暂存于内存中的,通过字节数组进行存储,当其数据量大小达到指定阀值之后,在开始

20、向 HFile 进行写入。写入成功后,需要再次开启一个全新的Block 来接收新的数据记录,该逻辑通过 HFileBlock.Writer 类的 startWriting 方法来封装,方法执行后,会首先开启 ByteArrayOutputStream 输出流实例,然后在将其包装成DataOutputStream 对象,用于向目标字节数组写入要添加的 Block 实体信息。在 HFile.Writer 内部,不同类型的数据块是通过不同的 Writer 进行写入的,其内部封装了3 种不同类型的子 Writer(这些 Writer 共用一个 FSDataOutputStream 用于向 HFile

21、写入Block 数据 ),分别如下: HFileBlock.Writer通过该 Writer 完成表格数据块(DataBlock)向 HFile 的写入逻辑,大致流程如下:每当执行 HFile.Writer 类的 append 方法进行添加数据时,会检测当前 DataBlock的大小是否已经超过目标阀值,如果没有,直接将数据写入 DataBlock,否则需要进行如下处理:1. 将当前 DataBlock 持久化写入 HFile写入之前需要首先生成目标数据块的 header 和 checksum 信息,其中checksum 信息可通过 ChecksumUtil 的 generateChecksu

22、ms 方法进行获取,而 header 信息可通过 putHeader 方法来生成。2. 生成当前 DataBlock 的索引信息索引信息是由索引 key,数据块在 HFile 中的偏移量位置和数据块的总大小3 部分信息组成的,其中索引 key 可通过 CellComparator.getMidpoint 方法进行获取,方法会试图返回一条数据记录能够满足如下约束条件:(1)索引 key 在排序上大于上一个 DataBlock 的最后一条记录;(2)索引 key 在排序上小于当前 DataBlock 的第一条记录;(3)索引 key 的 size 是最小的。经过这样处理之后能够整体降低索引块的数据

23、量大小,从而节省了内存空间的使用,并提高了加载效率。3. 将索引信息写入索引块通过 HFileBlockIndex.BlockIndexWriter 的 addEntry 方法。4. 判断是否有必要将 InlineBlock 进行持久化InlineBlock 包括叶子索引块和布隆数据块,它们的持久化逻辑分别通过BlockIndexWriter 和 CompoundBloomFilterWriter 来完成。5. 开启新的 DataBlock 进行数据写入,同时将老的数据块退役如果集群开启了 hbase.rs.cacheblocksonwrite 配置,需要将老数据块缓存至 BlockCache

24、 中。 HFileBlockIndex.BlockIndexWriter通过该 Writer 完成索引数据块(IndexBlock) 向 HFile 的写入逻辑。在 HFile 内部,索引数据是分层级进行存储的,包括根索引块、中间索引块和叶子索引块。其中叶子索引块又称之为 InlineBlock,因为它会穿插在 DataBlock 之间进行存储。同 DataBlock 类似, IndexBlock 一开始也是缓存在内存里的,每当DataBlock 写入 HFile 之后,都会向当前叶子索引块添加一条索引实体信息。如果叶子索引块的大小超过 hfile.index.block.max.size 限

25、制,便开始向 HFile 进行写入。写入格式为:索引实体个数、每条索引实体相对于块起始位置的偏移量信息,以及每条索引实体的详细信息(参考 Block 块结构)。这主要是叶子索引块的写入逻辑,而根索引块和中间索引块的写入则主要在HFile.Writer 关闭的时候进行,通过 BlockIndexWriter 的 writeIndexBlocks 方法。在 HFile 内部,每一个索引块是通过 BlockIndexChunk 对象进行封装的,其对内声明了如下数据结构:(1)blockKeys,封装每一条索引所指向的 Block 中第一条记录的 key 值;(2)blockOffsets,封装每一条

26、索引所指向的 Block 在 HFile 中的偏移量位置;(3)onDiskDataSizes,封装每一条索引所指向的 Block 在 HFile 中的长度。除此之外,根索引块还比较特殊,其对内声明了 numSubEntriesAt 集合,集合类型为 List,每当有叶子索引块写入 HFile 之后都会向该集合添加一条实体信息,实体的 index 为当前叶子索引块的个数, value 为索引实体总数。这样,通过numSubEntriesAt 集合便能确定 midKey(中间索引) 处在哪个叶子索引块上,在通过 blockKeys、 blockOffsets 和 onDiskDataSizes

27、便能够获取最后的 midkey 信息。然后将其作为根索引块的一部分写入 HFile,并通过 FixedFileTrailer 来标记根索引块的写入位置。需要注意的是根索引块的大小也是受上限约束的,如果其大小大于hfile.index.block.max.size 参数阀值 (默认为 128kb),需要将其拆分成多个中间索引块,然后在对这些中间索引块创建根索引,以此来降低根索引块的大小,具体逻辑可参考 BlockIndexWriter 类的 writeIntermediateLevel 方法实现。 CompoundBloomFilterWriter通过该 Writer 完成布隆数据向 HFile

28、 的写入逻辑。布隆数据在 HFile 内部同样是分块进行存储的,每一个数据块通过ByteBloomFilter 类来封装,负责存储指定区间的数据集映射信息( 参考布隆过滤器实现章节)。每当执行 HFile.Writer 的 append 方法向 DataBlock 添加 KeyValue 数据之前,都要调用 ByteBloomFilter 的 add 方法来生成该 KeyValue 的布隆映射信息,为了满足目标容错率,每个 ByteBloomFilter 实体能够映射的 KeyValue 数量是受上限约束的,如果达到目标上限值需要将其持久化到 HFile 中进行存储,然后开启新的ByteBlo

29、omFilter 实例来接管之前的逻辑。每当布隆数据块写入成功之后,都会执行 BlockIndexWriter 的 addEntry 方法来创建一条布隆索引实体,实体的 key 值为布隆数据块所映射的第一条 KeyValue 的key 值。同叶子索引块一样,布隆数据块也被称之为 InlineBlock,在写入 DataBlock 的同时会对该类型的数据块进行穿插写入。这主要是布隆数据块的写入逻辑,而布隆索引块主要是在 HFile.Writer 关闭的时候进行创建的,通过CompoundBloomFilterWriter.MetaWriter 的 write 方法,将布隆索引数据连同 meta信息一同写入 HFile。

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报