hbase:rowkey设计
一、基础概念
1、 Row Key: 行键,Table的主键,Table中的记录默认按照Row Key升序排序
2、Timestamp:时间戳,每次数据操作对应的时间戳,可以看作是数据的version number。
3、Column Family:列簇,Table在水平方向有一个或者多个Column Family组成,一个Column Family中可以由任意多个Column组成,即Column Family支持动态扩展,无需预先定义Column的数量以及类型,所有Column均以二进制格式存储,用户需要自行进行类型转换。
二、rowkey设计意义
hbase访问数据主要通过rowkey来访问,好的rowkey设计可以大幅度提高hbase数据库的性能。
三、设计原则
1、RowKey长度原则:RowKey是一个二进制码流,可以是任意字符串,最大长度为64KB,实际应用中一般为10~100bytes,存为byte[]字节数组,一般设计成定长。建议是越短越好,不要超过16个字节。原因一是数据的持久化文件HFile中是按照KeyValue存储的,如果RowKey过长比如100字节,1000万列数据光RowKey就要占用100*1000万=10亿个字节,将近1G数据,这会极大影响HFile的存储效率;原因二是memstore将缓存部分数据到内存,如果RowKey字段过长内存的有效利用率会降低,系统将无法缓存更多的数据,这会降低检索效率。因此RowKey的字节长度越短越好原因三是目前操作系统大都是64位,内存8字节对齐。控制在16个字节,8字节的整数倍利用操作系统的最佳特性。
2、RowKey散列原则:如果RowKey是按时间戳的方式递增,不要将时间放在二进制码的前面,建议将RowKey的高位作为散列字段,由程序循环生成,低位放时间字段,这样将提高数据均衡分布在每个RegionServer实现负载均衡的几率,如果没有散列字段,首字段直接是时间信息,将产生所有数据都在一个RegionServer上堆积的热点现象,这样在做数据检索的时候负载将会集中在个别RegionServer,降低查询效率。
3、RowKey唯一原则:必须在设计上保证其唯一性。
四、案例分析
1、我们在表中存储的是文件信息,每个文件有5个属性:
- 文件id(long,全局唯一)
- 创建时间(long)
- 文件名(String)
- 分类名(String)
- 所有者(User)
我们可以输入的查询条件:
- 文件创建时间区间(比如从20120901到20120914期间创建的文件)
- 文件名(“中国好声音”)
- 分类(“综艺”)
- 所有者(“浙江卫视”)
假设当前我们一共有如下文件:
ID | CreateTime | Name | Category | UserID |
---|---|---|---|---|
1 | 20120902 | 中国好声音第1期 | 综艺 | 1 |
2 | 20120903 | 中国好声音第2期 | 综艺 | 1 |
3 | 20120904 | 中国好声音第3期 | 综艺 | 1 |
4 | 20120905 | 加多宝凉茶广告 | 综艺广告 | 4 |
5 | 20120906 | 张玮独家专访 | 花絮 | 3 |
这里UserID应该对应另一张User表,暂不列出。我们只需知道UserID的含义:
- 1代表 浙江卫视
- 2代表 好声音剧组
- 3代表 XX微博
- 4代表 赞助商
调用查询接口的时候将上述5个条件同时输入find(20120901,20121001,”中国好声音”,”综艺”,”浙江卫视”)。
此时我们应该得到记录应该有第1、2、3、4、5、7条。第6条由于不属于“浙江卫视”应该不被选中。
我们在设计RowKey时可以这样做:采用UserID + CreateTime + FileID组成rowKey,这样既能满足多条件查询,又能有很快的查询速度。
需要注意以下几点:
(1)、每条记录的RowKey,每个字段都需要填充到相同长度。假如预期我们最多有10万量级的用户,则userID应该统一填充至6位,如000001,000002…
(2)、结尾添加全局唯一的FileID的用意也是使每个文件对应的记录全局唯一。避免当UserID与CreateTime相同时的两个不同文件记录相互覆盖。
按照这种RowKey存储上述文件记录,在HBase表中是下面的结构:
rowKey(userID 6 + time 8 + fileID 6) name category ….
00000120120902000001
00000120120904000002
00000120120906000003
00000120120908000004
00000120120910000005
00000120120914000007
00000220120912000006
00000220120916000008
00000320120918000009
00000420120920000010
怎样用这张表?
在建立一个scan对象后,我们setStartRow(00000120120901),setEndRow(00000120120914)。
这样,scan时只扫描userID=1的数据,且时间范围限定在这个指定的时间段内,满足了按用户以及按时间范围对结果的筛选。并且由于记录集中存储,性能很好。
然后使用SingleColumnValueFilter(org.apache.hadoop.hbase.filter.SingleColumnValueFilter),共4个,分别约束name的上下限,与category的上下限。满足按同时按文件名以及分类名的前缀匹配。
(注意:使用SingleColumnValueFilter会影响查询性能,在真正处理海量数据时会消耗很大的资源,且需要较长的时间。
出处 san_yun的博客