多少个column family合适
推荐是:1-3个(越少越好)
划分column family的原则
1.是否具有相似的数据格式
2.是否具有相似的访问类型
下面来看两个例子,解释上面的原则:
1.相同的rowkey,有一个很大的text数据需要存储,又有一个picture数据需要存储
对于很大的text数据我们肯定是想让它Compress后再存储
而picture的数据呢,我们并不想让他压缩后存储,因为对于这种二进制的数据压缩并不能节省空间
所以,我们可以将这两个数据分成两个column family来存储:
create ‘table’,{NAME => ‘t’, COMPRESSION => ‘SNAPPY’},{NAME => ‘p’}
2.有一张hbase表,需要存储每个用户的信息(比如名字、年龄等)和这个用户每天访问网站的信息
对于用户的信息,不经常变,而且量少
对于用户每天访问网站的信息是经常变化且数据量很大的
如果将这两种信息放在同一个column family中的话,用户每天访问网站的信息数据的增大导致会出现memory store的flush,然后会导致compaction,因为compaction是column family级别的,所以会将每个用户的信息(比如名字、年龄等)和这个用户每天访问网站的信息都合并到文件中
实际上用户的信息不大,且不经常变,没必要每次compaction都要将用户的信息写到磁盘中,导致资源的浪费
所以可以将用户的信息和用户每天访问网站的信息分成两个column family来存储
Table Schema的设计注意事项
1.每一个region的大小在10到50G(默认是10G)
2.每一个table控制在50-100个regions
3.每一个table控制在1到3个column family
4.每一个column family的命名最好要短,因为column family是会存储在数据文件中的
RowKey的设计原则
原则一
长度原则:
rowkey的长度一般被建议在10-100个字节,不过建议是越短越好(如果长度有点长,建议使用Block Encoding :DATA_BLOCK_ENCODING => ‘FAST_DIFF’)
原因:
1.数据持久化文件HFile是按照keyvalue存储的,如果rowkey过长,比如100个字节,1000万列数据光Rowkey就要占用100*1000万=10亿个字节,将近1G数据,这会极大影响HFile的存储效率
2.MemStore将缓存部分数据到内存,如果Rowkey字段过长内存的有效利用率会降低,系统将无法缓存更多的数据,这会降低检索效率。因此Rowkey的字节长度越短越好
3.目前操作系统是都是64位系统,内存8字节对齐。如果rowkey是8字节的整数倍的话,则利用了操作系统的最佳特性。
原则二
hbase特性: rowkey是按照字典顺序进行存储的
hbase中相似的rowkey会存储在同一个Region中
比如,我们的rowkey是网站的域名,如下:
www.apache.org
mail.apache.org
jira.apache.org
这样去存储,那么就不会分在一起
如果我们将域名反转作为rowkey的话更好点,如下:
org.apache.www
org.apache.mail
org.apache.jira
这样的话,我们查询的时候,就只会存在同一个region中,这样查询性能就会大大提升
原则三
因为rowkey是按照字典顺序存储的,所以如果rowkey没有设计好的话,会引发: Hotspotting(大量的请求只发往到一个Region中)
解决Hotspotting问题的三个方法
1.Salting((撒盐似的)散布、加盐)
创建一个表,这个表以 b c d 进行切分
create ‘test_salt’, ‘f’,SPLITS => [‘b’,‘c’,‘d’]
原始的rowkey是这样的:
boo0001
boo0002
boo0003
boo0004
boo0005
boo0003
撒盐后的rowkey是这样的:
a-boo0001
b-boo0002
c-boo0003
d-boo0004
a-boo0005
d-boo0003
这种方式虽然解决了热点问题,但是带来的问题就是在读的时候,会有额外的处理,因为你不知道你的key变成什么样去了,只有把所有情况拿到,然后再去处理,因此这种方式虽然解决了这个热点问题,但是依旧有其他的问题
2.Hashing
创建一个表(region4个,切分的算法采用HexStringSplit(十六进制)):
create ‘test_hash’, ‘f’, { NUMREGIONS => 4, SPLITALGO => ‘HexStringSplit’ }
原始的rowkey:
boo0001
boo0002
boo0003
boo0004
md5 hash rowkey(hash后的首字母一定是在0-F之间的):
4b5cdf065e1ada3dbc8fb7a65f6850c4
b31e7da79decd47f0372a59dd6418ba4
d88bf133cf242e30e1b1ae69335d5812
f6f6457b333c93ed1e260dc5e22d8afa
这种方式它不会向同一个Region写数据,从而解决了热点问题,在获取的时候也很方便,把rowkey hash后就是hbase中的rowkey
3.反转rowkey
create ‘test_reverse’, ‘f’,SPLITS => [‘0’,‘1’,‘2’,‘3’,‘4’,‘5’,‘6’,‘7’,‘8’,‘9’]
时间戳类型的rowkey:
1524536830360
1524536830362
1524536830376
反转rowkey:
0630386354251
2630386354251
6730386354251
以上几种方式,根据自己的业务场景去使用,推荐使用方式2
原则四
我们在设计RowKey时可以这样做:采用多个列组成RowKey,这样既能满足多条件查询,又能有很快的查询速度。比如:CTU到PEK,2019年08月21这是三个列,我们可以直接设置为rowkey(CTUPEK20190821,然后再hash),这样的话,既不会有热点问题,查询的时候,也可以满足很多条件,查询速度也很快了