HBase KeyValue格式解析
Posted yhxx511
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HBase KeyValue格式解析相关的知识,希望对你有一定的参考价值。
HBase的核心存储结构是KeyValue类。这个类定义了HBase的数据模型,并贯穿了HBase的整个读写链路。同时,HBase自身的元数据管理也是使用了业务表相同的模式。所以,从底层了解KeyValue的格式和设计,会加深我们对HBase基础架构的理解,从而更好的使用和管理HBase。
数据模型浅析
HBase的数据模型是一个松散表结构,所谓松散,包含两个方面的含义
- 没有schema:没有一个地方定义了一行应该包括哪些列,这些列都是什么类型。这个信息通常只有用户自己知道。
- 稀疏:每行的列都可以完全不同,行与行之间的列在HBase层面没有任何关联
所以,我们说HBase是schema-free的,可以任意添加列。这些能力的基础就是KeyValue的设计。
KeyValue对使用者而言是一个六元组,即(rowkey, family, qualifier, timestamp, type, value)。在1.x版本之后,添加了tags支持,变成了7元组,即(rowkey, family, qualifier, timestamp, type, value, tags)。但其设计思想是没有变的,即key-value的方式进行存储,从业务逻辑上看,key就是rowkey;value除了值本身,还包含了value的一些描述信息,即family、qualifier、timestamp和type。
所以,KeyValue本身在可以独立的描述一行中的一列数据。因为带上了列名信息,所以,不需要事先定义好一行有哪些列(schema)。也因为如此,一行中可以存在任意的列,每行的列都可以完全不同。这个能力相比传统的RDBMS而言无疑是非常强大的,目前诸多的NoSQL系统几乎都提供这种schema-free的能力。这个能力比较常见的应用可以是:
- 从容应对业务模型变化:设计数据库的人都知道,业务需求变了,表设计也要跟着变,经常要添加列。而HBase这种模型,不存在“添加列”这个操作,直接写新列就好了。
- 列名本身也可以存储信息:因为列名本身与列值绑定在一起了,我们可以利用列名来存储信息,比如
- 时序场景,可以用列名作为数据的时间
- 图数据库场景,可以用列名来描述“边”
天下没有免费的午餐,在获得上述强大能力的同时,要付出的代价也是巨大的,即数据冗余,包括:
- rowkey重复存储:一行由多个具有相同rowkey的KeyValue组成
- family,qualifier重复存储
如果表是直接从RDBMS迁移过来的,每行都有相同的列,那无疑列名的重复会额外占用很多空间,尤其是一行中列较多的时候。这也是为什么在表设计时,要选取尽可能短小的family名字和列名。另外,rowkey的重复也有同样的问题。我们有一些技术可以有效的解决这些问题。
- DIFF压缩:解决rowkey重复存储的问题,在一行中列较多时效果非常明显,这里不展开。
- 列名映射:通常列名都是一些比较长的单词或者短语,每列的列名不同,对DIFF压缩不友好。所以,可以将易读的列名映射为二进制的短列名(如short类型),HBase层面实际存储的是1,2,3这样的列名,而业务层通过一套列名映射机制在读写数据的时候进行列名转换。用时间换空间。具体可以参考Phoenix的Column Name Econding(PHOENIX-1598)。
下面,我们来看一下KeyValue的数据格式。
KeyValue格式(0.94)
KeyValue本身就是一串二进制数据,即byte[],通过一些编码规则,将二进制数据映射为六元组或七元组。下面,我们先看看094版本的KeyValue格式。
0.94版本的KeyValue的byte数组由3部分组成。
- 2个长度字段,每个字段4字节:即key的长度,value的长度
- key数据
- value数据
其中,key包括了rowkey,family,qualifier,timestamp,type,这5个部分。
- rowkey:2字节的rowkey length字段描述其长度,所以,最大的rowkey长度就是Short所能描述的最大正整数,即Short#MAX_VALUE,32KB。
- famliy:1字节的family length字段描述其长度,所以,最大的family长度是Byte#MAX_VALUE,即127字节
- qualifier:不独立存储其长度,通过KeyLength,rowkey长度,family长度,可以计算得到qualifier长度。
- timestamp:定长8字节,单位毫秒,时间戳
- type:1字节,描述这个KV的类型,如Put还是delete marker等
末尾是value字段,其长度由开始的ValueLength字段来定义。最大是Integer#MAX_VALUE,即4GB。但实际上,超过1MB的KV通常就会导致严重的性能问题了,超过64MB的KV一般来说Protobuffer很可能都无法支持其进行序列化和反序列化。所以,单纯从这个意义上看,HBase本身并不适合存储大对象(还有其他很多因素导致HBase不适合管理大块数据,这里不展开)。
由上面的KeyValue格式定义可见,这个格式还是很直观、符合直觉的,易理解,代码也容易懂,没有玩什么奇技淫巧。
KeyValue格式(1.x之后的版本)
与094版本相比,末尾多了一个tag区段,可以存储任意数量的tag。tag提供了一个扩展KeyValue能力的途径,比如行级/列级TTL,可以通过将TTL记录在tag中来实现。用户也可以存储一些自定义的信息。
其他
KeyValue的格式是了解HBase底层存储结构的第一步,还有其他一些关键的设计,包括:
- 各类KeyValue comparator的实现,及他们的应用
- DIFF压缩的原理,及其对读写链路的影响
- 时间戳与多版本的原理和应用
- KeyValue在HFile和HLog中的存储,即HLog的格式和HFile的格式
- 读链路:KeyValue的查找
- meta表(root表)的设计:HBase表设计的典范
- rowkey相关的一些列问题:rowkey的设计,前缀扫描,复合主键实现与数据类型编码,等等
- 。。。
后续会慢慢对这些问题和设计进行整理和分析。
以上是关于HBase KeyValue格式解析的主要内容,如果未能解决你的问题,请参考以下文章
记录一次 Hbase 线上问题的分析和解决,并分析总结下背后的知识点 - KeyValue size too large...
记录一次 Hbase 线上问题的分析和解决,并分析总结下背后的知识点 - KeyValue size too large...