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...

HQueue:基于HBase的消息队列

HashMap详解

大数据性能调优之HBase的RowKey设计

大数据性能调优之HBase的RowKey设计