如何设计高效的HBase数据模型

标签: 设计 hbase 数据模型 | 发表时间:2021-05-14 16:42 | 作者:Jowin
出处:https://www.infoq.cn

从学习和使用HBase的经历中,整理出对使用者而言,需要了解的HBase基础知识,Mark一下。

1.背景知识

1.1 数据模型

学习HBase/BigTable最困难的部分,是理解它的数据模型,换句话说它究竟是咋用的?在BigTable论文中明确说明:

A Bigtable is a sparse, distributed, persistent multidimensional sorted map.

论文做了进一步解释:

The map is indexed by a row key, column key, and a timestamp; each value in the map is an uninterpreted array of bytes.

推荐两篇文章,对此解释的非常清楚: understanding-hbase-and-bigtab" 和 Introduction to HBase Schema Design",下面是一个更形象的示例:

{ // ... "aaaaa" : { // Row Key "A" : { // Column Families "foo" : { // Column Qualifiers 15 : "y", // 15: Timestamp / Version number, "y": Cell Value 4 : "m" }, "bar" : { 15 : "d", } }, "B" : { "" : { 6 : "w" 3 : "o" 1 : "w" } } }, // ... } 拆解说明: map : 存储KeyValue数据。 persistent : 数据以文件的形式在HDFS(或S3)/GFS存储。 distributed : HBase和BigTable都建立在分布式文件系统之上,计算/存储分离架构;由Master和一组Storage Server组成,数据分区存储,典型的分布式系统。 sorted : RowKey按字典序排序的。 multidimensional: 如上面的示例看到的,这是一个嵌套Map,或者说一个多维Map。

真实的存储是平面文件结构,存储模型是类似下面的结构:

aaaaa, A:foo,15 ---> y aaaaa, A:foo, 4 ---> m aaaaa, A:bar, 15 ---> d aaaaa, B:, 6 ---> w aaaaa, B:, 3 ---> w aaaaa, B:, 1 ---> w Key的逻辑结构是: {RowKey:ColumnFamily:Qualifier:TimeStamp},Key按字母升序排序,TimeStamp按降序排序。

1.2 读/写/压缩

HBase/BigTable存储使用LSM方式实现,这个网上文章很多,这里只简单介绍需要了解的主要流程:

(1) 写流程:先写Log文件(WAL),然后写MemTable,当MemTable写满后,把数据落地到文件当中。 (2) 读流程:由于MemStore、HFiles中都包含数据,读取操作其实类似一个多路归并排序操作,最近的数据在MemStore 中,次新数据在近期生成的HFile中,老数据在更早生成的HFile中,按照这个顺序遍历要查找的key。 (3) 压缩流程:当文件越来越多时,就需要进行压缩,回收无效效数据(减少存储占用),减少文件数量(提高读效率)。 压缩操作的思路是合并MemStore和HFiles文件,删除无效的key值(被新版本覆盖或删除),生成新的文件,回收旧文件。

可以看出,HBase/BigTable非常适合有大量写操作的应用,顺序读性能也不错,适合批量数据处理(例如MapReduce)。BigTable论文提到的其中两个使用场景都符合这个特征:

(1) 存储抓取回来的网页,使用MapReduce进行处理; (2) Google Analytics项目,收集用户点击数据,使用MapReduce定期分析,生成网站访问报告。

1.3 存储分区

HBase支持数据自动分区,分区方式: 水平分割+垂直分割,

水平分割:KeyValue数据依据RowKey划分到不同的Region,每一个Region被分配给一个Region Server,具备良好的扩展性。当一个Region过大时,会被分割成两个Region。当一个Region Server负载过重时,把其中的部分Region迁移到其他Region Server。垂直分割:在一个Region内部,每个ColumnFamily的数据是单独存储的,这使他们有更好的访问局部性。

参考http://hbase.apache.org/book.html#trouble.namenode.hbase.objects,HBase在HDFS存储数据的目录结构如下:

/hbase /data / (Namespaces in the cluster) / (Tables in the cluster) / (Regions for the table) / (ColumnFamilies for the Region for the table) / (StoreFiles for the ColumnFamily for the Regions for the table)

1.5 KeyValue存储格式

The KeyValue format inside a byte array is:

keylengthvaluelengthkeyvalue

在新版本中,支持为Cell附加tags,KeyValue的格式为: {keylength,valuelength, key, value, tags}。

The Key is further decomposed as:

rowlengthrow (i.e., the rowkey)columnfamilylengthcolumnfamilycolumnqualifiertimestampkeytype (e.g., Put, Delete, DeleteColumn, DeleteFamily)

行键和列族/列属性,会编码到每一个行当中,所以行键、列族和列属性应该尽可能短一些。

1.4 访问模型和事务

HBase主要使用Put/Delete/Scan这几个接口访问数据,支持批量读写:

Version/TimeStamp:HBase/BigTable支持一个Key的多个Value版本(依靠TimeStamp区分),查询的时候可以访问最新版本,也可以访问指定时间区间的所有版本。Column:每一个Column Family都可以有任意多个Column,所以必须指定要访问的Column Qualifiers,才能确认要访问的数据。

HBase可以保证对同一个Row的操作是原子操作,这是因为:

Row只属于一个Region,只能通过一个Region Server进行写操作,不存在写冲突;通过WAL,可以保证对一个Row的多个操作(可以是多个ColumnFamily)要么都成功,要么都失败。一个Region Server的多个Region共享一组WAL日志文件,一个Batch操作做为一个Record写入WAL,所以可以保证要么 都成功,要么都失败。

The HDFS directory structure of HBase WAL is.. /hbase /WALs /(RegionServers) / (WAL files for the RegionServer)

1.5 TTL

可以对表和列族设置TTL(秒),HBase会自动删除过期的Rows;在新近的版本中,HBase支持对每个Cell单独设置TTL(毫秒)。

(1) 标和列族的TTL保存在Schema中,属于表/列族级别的配置。 (2) Cell的TTL是作为tag,编码保存在Cell里面的,因此每个Cell都可以单独设置。

1.6 版本数量

HBase支持多版本,一个RowKey可以有多个版本的Value,使用时间戳区分版本。

Maximum Number of Versions。 一般不建议设置成很大的值保(例如几百或更多),除非这些旧版本的数据非常有价值,因为这会使StoreFile大小剧增。缺省值是1。Minimum Number of Versions。这个值和ttl参数一起使用,实现类似“保留最后T分钟的数据,最多N个版本,但至少保留M个版本”的需求。缺省值是0,也就是不启用这个feature。

2. 模式设计指南

HBase本质上是一个kv数据库,不支持RDBMS中常见的索引、事务等特性,它的设计目标是应对高吞吐量、海量数据扩展性的需求。

应用程序应该根据业务需求,来设计高层数据模式。

2.1 梳理数据访问模式

访问模式直接决定我们的设计方案,仔细梳理数据访问模式,列出主要访问场景,在后面设计中时时回看,我们的设计能否满足这些访问场景?还有没有更高的设计方案?

2.2 设计行键

RowKey是HBase表设计中最重要的事情,它决定了应用与HBase交互的方式,直接影响数据访问的性能。

(1) 行键,列族,列属性尽可能短,以节省存储空间 (2) 利用行键排序的特性,使用相同key前缀聚合经常一起访问的数据,提高读效率 例如,对于网页URL,可以对域名做字符串反转,得到com.demo.xxx/page1.html,使一个域名下的页面在存储上相邻。 (3) 充分发挥分区的并发性能,避免行键设计导致出现热门分区 常见的方法: - salt:通过增加不同的前缀,使Key均匀分布在各个region。 - hash:对key值做hash,这可以使key的分布非常均匀,并且可以得到固定长度(例如md5),缺点是不方便做区间遍历。 (4) 尽量使用单行设计,避免多行事务 考虑如何在单个API调用中完成访问而不是多个API调用,HBase没有跨行事务,避免在客户端代码中构建这种逻辑。 (5) 对访问最近数据的场景,使用逆序时间戳 数据库的一个场景问题是快速查找数据的最新版本,解决这个问题的一个方法是利用Hadoop的key值有序这个特征,把时间戳逆序后append到key值尾部。 key格式: [key][reverse_timestamp]`,其中`reverse_timestamp = Long.MAX_VALUE - timestamp

2.3 设计列族和列属性

(1) 一个典型的模式每个表有1到3个列族。 (2) 把访问模式相似的列放到一个列族中。 (3) 使列族名称尽可能短,最好是一个字符。 (4) 使用更短的列属性(例如"via", 而不是"myVeryImportantAttribute"这样冗长的列属性)。 (5) 列属性也可以用来存储数据,就像Cell一样。 (6) 使cell小于10MB;在使用mob时,不要超过50MB;否则,考虑把cell存到HDFS中,在hbase中仅保存一个指针。

2.4 规划分区

(1) 使region大小保持在10~50GB (2) 对于包含1或2个列族的表来说,大约50-100个区域是一个很好的数字。 (3) 对于RowKey为:`设备ID+时间戳`格式的时序数据,可以允许更多的分区,因为历史数据分区通常是不活动的。 (4) 预分区 Hadoop支持自动分区和负载均衡,但如果你非常了解你的数据,对table预先(人工)分区通常是一种最佳实践。 但要小心测试你的数据,务必把key均匀分布在各个region中,避免负载倾斜。 注意:使用Bytes.split (which is the split strategy used when creating regions in Admin.createTable(byte[] startKey, byte[] endKey,numRegions)分区时,要注意它是按照 Byte范围来分割的(包括了不可见字符),可能会导致有些Region根本不会有key存进来。

3.案例解析-OpenTSDB

OpenTSDB使用HBase存储数据,它的两个核心设计思想:

(1) 使用一行存储一个时间区间的所有数据

OpenTSDB把1小时内的事件作为一行存储,所有事件存储在列族t中,Key中的时间戳精确到小时,列名称为以本小时起始的秒数(1小时3600秒,最多也就3600个事件),或毫秒数(1小时可以容纳的事件数就多了)。

在当前时间段过去以后,可以把1个小时内的数据压缩到一个列中存储(对照KeyValue结构,这能节省太多的冗余数据)。

(2) 对字符串编码,减小key的长度

TSDB把字符串映射成数值,来减小Key的长度。指标名称,标签Key,标签Value,都映射成一个变长整数(使用映射表:tsdb-uid 存储文本和整数的映射关系),这就使得Key值非常短。

RowKey结构:`<指标名称><时间戳><标签1><标签2>`。 注: (1) 时间戳:指明时间点 (2) 指标名称: 这个数据的抽象概括,指明监控内容,如温度,湿气,大小 (3) 标签: 对象,指明监控对象 ,如某个城市,某个CPU,某块区域 (4) 值: 指标数值

图1 OpenTSDB tsdb表结构

参考:

(1) Bigtable: A Distributed Storage System for Structured Data"

(2) Apache HBase Reference Guide"

(3) understanding hbase andbigtab"

(4) Introduction to HBase Schema Design"

(5) OpenTSDB HBase Schema"

(6) Hbase最佳实践系列

HBase最佳实践之集群规划" HBase最佳实践之列族设计优化" HBase最佳实践之读性能优化策略" HBase最佳实践之写性能优化策略" HBase最佳实践-管好你的操作系统"

相关 [设计 hbase 数据模型] 推荐:

如何设计高效的HBase数据模型

- - InfoQ推荐
从学习和使用HBase的经历中,整理出对使用者而言,需要了解的HBase基础知识,Mark一下. 学习HBase/BigTable最困难的部分,是理解它的数据模型,换句话说它究竟是咋用的. 在BigTable论文中明确说明:. The map is indexed by a row key, column key, and a timestamp; each value in the map is an uninterpreted array of bytes.

HBase表设计

- - 互联网 - ITeye博客
默认情况下,在创建HBase表的时候会自动创建一个region分区,当导入数据的时候,所有的HBase客户端都向这一个region写数据, 直到这 个region足够大了才进行切分. 一种可以加快批量写入速度的方法是通过预先创建一些空的regions,这样当数据写入HBase时,会按 照 region分区情况,在集群内做数据的负载均衡.

HBase Schema 设计

- - IT瘾-dev
HBase 与传统关系数据库(例如MySQL,PostgreSQL,Oracle等)在架构的设计以及为应用程序提供的功能方面有很大的不同. HBase 权衡了其中一些功能,以实现更好的可扩展性以及更灵活的模式. 与关系数据库相比,HBase 表的设计有很大的不同. 下面将通过解释数据模型向您介绍 HBase 表设计的基础知识,并通过一个例子深入探讨 HBase 表的设计.

HBase RowKey 设计

- - IT瘾-dev
1.1 RowKey对查询的影响. HBase中 RowKey 用来唯一标识一行记录. 在 HBase 中检索数据有以下三种方式:. 通过 get 方式,指定 RowKey 获取唯一一条记录. 通过 scan 方式,设置 startRow 和 endRow 参数进行范围匹配. 全表扫描,即直接扫描整张表中所有行记录.

HBase Rowkey 设计指南

- -
为什么Rowkey这么重要. RowKey 到底是什么. 如果想及时了解Spark、Hadoop或者Hbase相关的文章,欢迎关注微信公共帐号: iteblog_hadoop. 我们常说看一张 HBase 表设计的好不好,就看它的 RowKey 设计的好不好. 可见 RowKey 在 HBase 中的地位.

Cassandra数据模型设计案例分析-转载

- - 人月神话的BLOG
原文:http://www.lamfire.com/?p=313. 近来 Cassandra 备受瞩目,很多人正在评估是否可以应用 Cassandra. 由于这些人更多的追求速度,相应的,我们的文档就过于粗浅了. 这些文章中,最差的是为有关系数据库基础的人解释Cassandra数据模型的那些. Cassandra 数据模型实际和传统的数据库差异非常大,足够让人眩晕,而且很多误解都需要修正.

hbase rowkey 设计(三维有序)

- - 经验沉淀 知识结晶
此文原创,转载请说明出处:http://ronxin999.blog.163.com/blog/static/4221792020130109202973/. 看这篇文章,你首先要了解hbase的基本存储模型,不懂的可以看我的文章,有做特别的说明. 今天难得有时间,写博文,特地总结下一直想写hbase的实践经验,在用hbase的过程中,我们都知道,rowkey设计的好坏,是我们能最大发挥hbase的架构优势,也是我们是否正确理解hbase的一个关键点.

HBase的一些应用设计tip

- - BlogJava_首页
1,对于HBase的存储设计,要考虑它的存储结构是:rowkey+columnFamily:columnQualifier+timestamp(version)+value = KeyValue in HBase,一个KeyValue依次按照rowkey,columnkey和timestamp有序.

Solr与HBase架构设计 - aitanjupt

- - 博客园_首页
摘要:本篇是本人在做一个大数据项目. ,对于系统架构总结的一点想法,如何在保证存储量的情况下,又能保证数据的检索速度. 前提:      Solr、SolrCloud提供了一整套的数据检索方案,HBase提供了完善的大数据存储机制. 需求:      1、对于添加到HBase中的结构化数据,能够检索出来.

HBase表设计原则整理

- - 互联网 - ITeye博客
因为每个列簇是存在一个独立的HFile里的,flush和compaction操作都是针对一个Region进行的,当一个列簇的数据很多需要flush的时候,其它列簇即使数据很少也需要flush,这样就产生的大量不必要的io操作. 在多列簇的情况下,注意各列簇数据的数量级要一致. 如果两个列簇的数量级相差太大,会使数量级少的列簇的数据扫描效率低下.