文章目录
前言
在关系型数据库中,表与表之间的关系有多种,一对一,一对多以及多对多的关系,那在hbase中这种关系怎么设计呢?
- 一对一:一个订单对应一笔支付。
- 一对多:一笔订单对应多个多个商品。
- 多对多:一个用户对应多个角色,一个角色对应多个用户。关系型数据库中一般实现
一对一,两个表之间之间关联,一对多一个主表和一个字表,多对多一般建立一个表来存两个表之间的关系。
1.Hbase表设计
1.1 RowKey的设计
Hbase中的row key是按照字母顺序排序的。这种设计方便Scan查询数据,进行相关行的读取与存储。 HBase中row key用来检索表中的记录,支持以下三种方式
- 通过单个row key访问:即按照某个row key键值进行get操作;
- 通过row key的range进行scan:即通过设置startRowKey和endRowKey,在这个范围内进行扫描;
- 全表扫描:即直接扫描整张表中所有行记录。
官方文档提供了三种设计方法
加盐
hash
反转
1.2 列族
不要在一张表里定义太多的column family。目前Hbase并不能很好的处理超过2~3个column family的表。因为某个column family在flush的时候,它邻近的column family也会因关联效应被触发flush,最终导致系统产生更多的I/O。
1.3 时间差反转 Reverse Timestamps
在实际中经常需要找到最近的版本的值,使用时间戳反转技术(Long.MAX_VALUE - timestamp)能够很好的解决这个问题。[key][reverse_timestamp].
其他的设计根据需要在官网进行查阅。
2.表设计案例
需求:CSDN发博客表,关注列表和粉丝列表。
2.1 关注列表和粉丝列表
关注列表和粉丝列表是一个多对多的关系,博主可以有多个粉丝,也可以关注多个人。
博主 关注列表 粉丝列表
Blogger1 Blogger3 Blogger2,Blogger3
Blogger2 Blogger3,Blogger4 Blogger1
Blogger3 Blogger1 Blogger2,Blogger4
Blogger4 Blogger5 Blogger1,Blogger2,Blogger3
表设计
csdn为每一位博主分配一个ID
Rowkey cf1:关注列表 cf2:粉丝列表
CSDN_1 cf:CSDN_3=bob cf:CSDN_2=elite,cf:CSDN_3=bob
CSDN_2 cf:CSDN_3=bob cf:CSDN_2=elite,cf:CSDN_3=bob
CSDN_3 cf:CSDN_1=jakson cf:CSDN_2=elite,cf:CSDN_4=Eric
CSDN_4 cf:CSDN_5=tom cf:CSDN_1=jakson,cf:CSDN_2=elite,CSDN_3=bob
2.2 博客表
Hbase中Rowkey的是根据字母顺序进行排序的,需要倒序进行排序,需要用到时间戳反转来设计rowkey,最新发布的博客在前。
表设计
csdn为每一位博主分配一个ID,Rowkey的设计根据时间戳来设计。
Rowkey cf:博客信息
CSDNIID+Long.MAX_VALUE - timestamp1 cf:title=‘大数据’,cf:content=‘大数据…’,cf:分栏=‘大数据.’
CSDNIID+Long.MAX_VALUE - timestamp2 cf:title=‘Java’,cf:content=‘HashMap的原理…’,cf:分栏=‘java’
CSDNIID+Long.MAX_VALUE - timestamp3 cf:title=‘python’,cf:content=‘大数据…’,cf:分栏=‘python’
CSDNIID+Long.MAX_VALUE - timestamp4 cf:title=‘人工智能’,cf:content=‘AI应用…’,cf:分栏=‘AI’
在hbase shell中创建博客表
create 'blog','cf'
假设一年发表10000篇博客,使用hbase客户端插入。
/**
* 假设一个博主2022年发表10000篇博客
* rowkey=博主ID_时间戳
*/
@Test
public void insertBlogData() throws Exception {
List<Put> puts = new ArrayList<>();
String bloggerId= "123";
for(int j = 0 ;j<10000;j++){
String title = "title_"+j;
String content = "content_"+j;
String specaification = "bigdata_"+j;
//rowkey
String date = getDate("2022");
String rowkey = bloggerId+"_"+(Long.MAX_VALUE-sdf.parse(date).getTime());
Put put = new Put(Bytes.toBytes(rowkey));
put.addColumn(Bytes.toBytes("cf"),Bytes.toBytes("title"),Bytes.toBytes(title));
put.addColumn(Bytes.toBytes("cf"),Bytes.toBytes("content"),Bytes.toBytes(content));
put.addColumn(Bytes.toBytes("cf"),Bytes.toBytes("specaification"),Bytes.toBytes(specaification));
puts.add(put);
}
table.put(puts);
}
查询12月份发表的博客
/**
* 查询某近2022年12月发表的博客
*/
@Test
public void scanByCondition() throws Exception {
Scan scan = new Scan();
String startRow = "123_"+(Long.MAX_VALUE-sdf.parse("20221231235959").getTime());
String stopRow = "123_"+(Long.MAX_VALUE-sdf.parse("20221201000000").getTime());
scan.withStartRow(Bytes.toBytes(startRow));
scan.withStopRow(Bytes.toBytes(stopRow));
ResultScanner rss = table.getScanner(scan);
for (Result rs:rss) {
System.out.print(Bytes.toString(CellUtil.cloneValue(rs.getColumnLatestCell(Bytes.toBytes("cf"),Bytes.toBytes("title")))));
System.out.print("--"+Bytes.toString(CellUtil.cloneValue(rs.getColumnLatestCell(Bytes.toBytes("cf"),Bytes.toBytes("content")))));
System.out.println("--"+Bytes.toString(CellUtil.cloneValue(rs.getColumnLatestCell(Bytes.toBytes("cf"),Bytes.toBytes("specaification")))));
}
}