Hbase的表结构设计与关系型数据库有很多不同,主要是Hbase有Rowkey和列族、timestamp这几个全新的概念,如何设计表结构就非常的重要。
创建
Hbase就是通过 表 Rowkey 列族 timestamp确定一行数据。
这与关系型数据库完全不同:
属性 | HBase | RDBMS |
---|---|---|
数据类型 | 只有字符串 | 丰富的数据类型 |
数据操作 | 简单的增删改查 不支持join | 各种函数和表连接 |
存储模式 | 基于列式存储 | 基于表格结构和行式存储 |
数据保护 | 更新后仍然保留旧版本 | 替换 |
可伸缩性 | 轻易的增加节点,兼容性高 | 需要中间层,牺牲功能 |
所以Hbase需要考虑的因素有:
1、这个表应该有多少列族
2、列族使用什么数据
3、每个列族有多少列
4、列名是什么
5、单元应该存放什么数据
6、每个单元存储多少时间版本
7、Rowkey结构是什么,应该包含什么信息
需要注意的点:
1、Join
Hbase中没有join 所以需要大表结构 行记录加关键字 解决这个问题
2、Rowkey
Rowkey设计非常重要 由于Hbase是有序的 需要考虑前缀后缀问题
可以通过Hbase Shell和 Java Api创建:
Configuration config = HBaseConfiguration.create();
Admin admin = new Admin(conf);
TableName table = TableName.valueOf("myTable");
admin.disableTable(table);
HColumnDescriptor cf1 = ...;
admin.addColumn(table, cf1); // adding new ColumnFamily
HColumnDescriptor cf2 = ...;
admin.modifyColumn(table, cf2); // modifying existing ColumnFamily
admin.enableTable(table);
Rowkey设计
Rowkey是不可分割的字节数组,按字典序存储在表中。
由于:Region基于Rowkey为一个区间的行提供服务 HFile在硬盘上存储有序的行 所以Rowkey就极大的影响了Hbase的性能。
Rowkey就是索引,如果不清楚Rowkey就只能扫描全表,那么性能将会大幅度下降。
这里用影片热度排行榜举例:
1、Rowkey是以字典序从大到小
原生Hbase只支持从小到大排序,要想实现从大到小,可以采用 Rowkey=Integer.MAX_VALUE-Rowkey的方式,在应用层再转回来完成需求。
2、Rowkey尽量散列
Rowkey要尽量散列,这样可以保证数据不在一个Region上,从而避免了读写的集中。
比如我们可以设计 userid_videoid 拼接字符串 这样的话user就会不均匀。
有三种办法解决: 反转userid 散列userid 将userid取模后进行MD5加密 取前6位加入userid中
3、Rowkey长度要尽量短
Rowkey过长,存储开销会大。
Rowkey过长,会导致内存的利用率降低,进而降低索引命中率。
列族
列族是一些列的集合,一个列族所有成员都有同样的前缀,比如courses:history 和 courses:math都是courses列族的成员。冒号是分隔符。列族前缀必须是可输出字符,列可由任意字节数组组成。
列族必须在表建立的时候声明,列则不需要特别声明,用户随时可以创建新列。
经验法则:
- 目标是把 region 的大小限制在 10 到 50 GB 之间。
- 目标是限制 cell 的大小在 10 MB 之内,如果使用的是 mob类型,限制在 50 MB 之内。否则,考虑把 cell 的数据存储在 HDFS 中,并在 HBase 中存储指向该数据的指针。
- 典型的 scheme 每张表包含 1 到 3 个列族。HBase 表设计不应当和 RDBMS 表设计类似。
- 对于拥有 1 或 2 个列族的表来说,50-100 个 region 是比较合适的。请记住, region 是列族的连续段。
- 保持列族名称尽可能短。每个值都会存储列族的名称(忽略前缀编码)。它们不应该像典型 RDBMS 那样,是自文档化,描述性的名称。
- 如果你正在存储基于时间的机器数据或者日志信息,并且 row key 是基于设备 ID 或者服务 ID + 时间,最终会出现这样一种情况,即更旧的数据 region 永远不会有额外写入。在这种情况下,最终会存在少量的活动 region 和大量不会再有新写入的 region。对于这种情况,可以接受更多的 region 数量,因为资源的消耗只取决于活动 region。
- 如果只有一个列族会频繁写,那么只让这个列族占用内存。当分配资源的时候注意写入模式。
实例
店铺与商品
店铺shop 商品 item 是多对多的关系
RDBMS表结构设计:
商铺表:
列名 | 列含义 |
---|---|
id | 主键 |
name | 店铺名称 |
address | 所在地 |
regdate | 注册日期 |
商品表:
列名 | 列含义 |
---|---|
id | 主键 |
name | 商品名称 |
price | 价格 |
details | 商品详情 |
title | 展示名称 |
关系表:
列名 | 列含义 |
---|---|
shop_id | 店铺主键 |
item_id | 商品主键 |
type | 关联类型 |
Hbase表结构设计:
店铺表:
商品表:
微博用户与粉丝
用户与粉丝是一对多
RDBMS表结构设计:
用户表:
列名 | 列含义 |
---|---|
id | 主键 |
nickname | 用户名 |
粉丝对应表:
列名 | 列含义 |
---|---|
user_id | 用户id |
fans_id | 粉丝id |
Hbase表结构设计:
更多实时计算,Hbase,Flink,Kafka等相关技术博文,欢迎关注实时流式计算