1. rowkey的设计
RowKey可以是任意字符串 ,最大长度64KB,实际应用中一般为10~100bytes,字典顺序排序,rowkey的设计至关重要,会影响region分布,如果rowkey设计不合理还会出现region写热点等一系列问题。
rowkey设计原则:
-
保证rowkey的唯一性:性质与主键唯一一致。
-
能满足需求的情况下,长度越短越好:推荐16字节。
-
高位散列:高位散列的目的是使数据均匀分布到不同的region上,散列方式一般采用"反转"、"加盐"、"MD5"的方式对高位进行处理。(防止写热点问题)
需求:hbase存储的是用户的交易信息, 我想查某个用户在某个时间段内的交易记录,如何设计rowkey
用户id(md5), 用户名称, 交易时间, 交易金额, 交易说明
用户id(md5), 交易时间
rowkey设计: 用户id(md5) + _ + 交易时间
sql
create 'hainiu:flow', 'cf'
put 'hainiu:flow', '02f5adff232b37422fc846cc5c1d8328_20211210110000', 'cf:name', 'user1'
put 'hainiu:flow', '02f5adff232b37422fc846cc5c1d8328_20211210110000', 'cf:amt', '1000'
put 'hainiu:flow', '02f5adff232b37422fc846cc5c1d8328_20211210110000', 'cf:time', '2021-12-10 11:00:00'
put 'hainiu:flow', '02f5adff232b37422fc846cc5c1d8328_20211210120000', 'cf:name', 'user1'
put 'hainiu:flow', '02f5adff232b37422fc846cc5c1d8328_20211210120000', 'cf:amt', '2000'
put 'hainiu:flow', '02f5adff232b37422fc846cc5c1d8328_20211210120000', 'cf:time', '2021-12-10 12:00:00'
put 'hainiu:flow', '02f5adff232b37422fc846cc5c1d8328_20211210130000', 'cf:name', 'user1'
put 'hainiu:flow', '02f5adff232b37422fc846cc5c1d8328_20211210130000', 'cf:amt', '3000'
put 'hainiu:flow', '02f5adff232b37422fc846cc5c1d8328_20211210130000', 'cf:time', '2021-12-10 13:00:00'
put 'hainiu:flow', '02f5adff232b37422fc846cc5c1d8328_20211210140000', 'cf:name', 'user1'
put 'hainiu:flow', '02f5adff232b37422fc846cc5c1d8328_20211210140000', 'cf:amt', '4000'
put 'hainiu:flow', '02f5adff232b37422fc846cc5c1d8328_20211210140000', 'cf:time', '2021-12-10 14:00:00'
# 查询 某个人在 20211210 日 11 点 到 20211211 日 12:30 间的交易记录
scan 'hainiu:flow', {STARTROW => '02f5adff232b37422fc846cc5c1d8328_2021121011' , STOPROW=> '02f5adff232b37422fc846cc5c1d8328_202112101230'}
我们可以发现数据已经可以按照范围查询了。
有的时候我们的单点查询比较频繁 ,那么我们将数据按照散列形式打散 然后穿插到不同的region中可以有效的防止读和写热点问题。
有时候我们查询的数据是范围性的扫描 ,这样时候我们就要知道数据必须要有相似的前缀 ,这样非常好按照范围查询,防止多region扫描问题的产生,比如人口普查数据,我们最好按照省份开头一样,这样的数据范围性比较好查询。
但是这个时候会出现数据倾斜或者热点问题,所以我们在这个基础上还可以实现预分区的设计 ,在设定表的时候指定分区的数据范围,保证数据的分布均匀。
2. hbase的预分区
为了解决数据的倾斜问题 ,或者数据在刚开始插入的数据都在一个region中,使得一个region中的压力太大 ,我们可以预先设定一个表数据的分区范围 ,让数据更加均匀的分布在不同的分区 中,或者我们在做数据分类的时候可以按照不同的类别将数据放入到不同的region中扫面数据的时候会比较容易,防止跨多个分区进行操作查询。
预分region需要考虑两个因素,即region个数与region大小。
- region个数
官方推荐region个数计算公式:
java
(RS Xmx * hbase.regionserver.global.memstore.size) / (hbase.hregion.memstore.flush.size * column familys)
其中:
**RS Xmx:**regionserver堆栈内存大小,官方推荐每台regionserver内存大小设置20-24G,不推荐设置更大,因为更大的堆栈内存GC效率较低。
**hbase.regionserver.global.memstore.size:**为整个regionserver中memstore总大小占用总内存的比例,一般默认为0.4
**hbase.hregion.memstore.flush.size:**为memstoreflush阈值,一般默认128,可以自己设置
**column familys:**为列族数
例:(20G*0.4)/(128M*2)=32
官方推荐每个regionserver上region个数在20-200之间。
- region大小
单个region官方推荐大小为5-10GB,可以通过hbase.hregion.max.filesize设置,当超过该值后会触发split,与region split策略相关。
java
# 首先我们需要创建预分区文件
# 比如我们做人口普查,需要将不同省份的数据放入到不同的region中
河北省,山西省,吉林省,辽宁省,黑龙江省,陕西省,甘肃省,青海省,山东省,福建省,浙江省,台湾省,河南省,湖北省,湖南省,江西省,江苏省,安徽省,广东省,海南省,四川省,贵州省,云南省
#首先我们按照这些省份的字典顺序将字母排序
云南省
台湾省
吉林省
四川省
安徽省
山东省
山西省
广东省
江苏省
江西省
河北省
河南省
浙江省
海南省
湖北省
湖南省
甘肃省
福建省
贵州省
辽宁省
陕西省
青海省
黑龙江省
# 然后将这些数据放入到一个文件中 /home/hadoop/split.txt
create 'hainiu:advance_split_region', 'cf', {SPLITS_FILE => '/home/hadoop/split.txt'}
由图,存在24个分区。
3. hbase的压缩
建表时指定压缩格式,开启压缩后可以非常有效的缓解hbase数据膨胀问题。
java
create 'hainiu:flow',{NAME => 'cf',VERSIONS => 3,COMPRESSION => 'SNAPPY'}, {SPLITS_FILE => '/tmp/advance_split_region_file'}
如果建表没指定压缩格式,那需要修改列族支持,步骤如下:
1) disable 'hainiu:flow'
如果表的数据量很大,region很多,disable过程会比较缓慢,需要等待较长时间。过程可以通过查看hbase master log日志监控。
2) alter 'hainiu:flow', NAME => 'cf', COMPRESSION => 'snappy'
NAME即column family,列族 。HBase修改压缩格式,需要一个列族一个列族的修改。名字一定要与你自己列族的名字一致,否则就会创建一个新的列族并且压缩格式是snappy的。
3)enable 'hainiu:flow'
重新enable上线flow表
4)major_compact 'hainiu:flow'
enable表后,HBase表的压缩格式并没有生效,还需要执行一个命令,major_compact。
Major compact除了做文件Merge操作,还会将其中的delete项删除。