HBase实战-RowKey优化经验
本文是结合作者在近做监控项目中将监控计算数据存入HBase中,并在设计HBase实践过程中反复思考了各种不同Rowkey设计方式在项目中应用的“利弊”,主要参考资料为HBase 官方文档。
一 : HBase的数据模型
- 逻辑模型
HBase是一个类似GoogleBigTable的开源的分布式数据库,大部分特性和BigTable相同,可以理解为是一个稀疏的,长期存储的,多维度和排序的映射表,表中的每一行可以有不同的列
HBase中基本的单位是列,一列或者多列构成了行,行有行键( RowKey ),每一行的RowKey都是的,相同行键的put操作被认为是对该行的更新操作。
HBase的表有若干行,每行有很多列,列中的值有多个版本,每个版本的值称之为一个单元格,每个单元格存储的不同时刻该列的值
- 物理模型
在逻辑上来说,HBase表是稀疏的行的集合,但是在物理上,HBase表是按列分开存储的。首先,按照列来划分列族(创建表的时候指定),存储每一行的不同列的物理文件,一个列族的数据存放在多个HFile中,一个列族的数据会被同一个region管理,物理上存放在一起。
二 :关于RowKey设计几点思考与建议
- RowKey的排序
HBase的RowKey排序是根据自小到大的排序规则,排序的比较方式是使用ASCII码的大小对比,这里要知道排序规则从小到大,结合自身的业务场景进行设计,比如业务要根据时间读取到新的一条数据那么就可以考虑翻转timestamp
- 热点问题
所谓热点问题,就是由于RowKey的设计不合理,导致数据往同一个Region上面读写消息,那么这无疑对单个节点的regionServer的压力是很大的,那么如何解决热点问题,一般会考虑如下几种方式:
01 ) Hash散列设计
使用一个Hash 值作为RowKey的前缀,Hash可以将负载均衡的分配到集群中,如果实现Hash散列,方式有很多,比如:将某一个字段假如是host字段取Hash之后放在rowkey的前面,或者说通过MD5Hash之后取前几位放在Rowkey的前面
这种方式可以很好的将rowkey均衡的分布到region上面,不会导致单个region的压力增大
02 ) 时间戳Timestamp
对于时间要求比较高的读来说,可以选择在Rowkey后面添加timestamp,比如我们就是根据时间去查询1分钟,1小时,1天内的数据,那么这个就可以考虑在Rowkey中加入timestamp
03 ) 翻转timestamp
翻转timstamp并将tiamestamp将在前面,可以有效的保证条数据是新的,这样当要get新的一条数据的时候,根据条件读取条数据即可,RowKey= Host + Item + Long.MAX_VALUE - timestamp
04)关于RowKey的长度
如果Rowkey太长,存储开销会增加,影响存储效率;第二内存中RowKey字段过长,会导致内存的利用率降低,降低了索引的命中率,参考下面的优化建议
05) Salting
Salting与加密无关,它指的是将随机数放在行键的起始处,salting给每一行键随机指定了一个前缀来让它与其它行键有着不同的顺序,比如:
假设行键的列表如下,表按照每个字母对应一个region来分割。前缀‘a’是一个region,‘b’就是另一个region。在这张表中,所有以‘f’开头的行都属于同一个region。这个例子关注的行和键如下:
foo0
foo1
foo2
foo3
现在将它们分散到不同的region上,就需要用四种不同的salts:a,b,c,d。每个字母前缀都对应着不同的region,从而行键就变为
a-foo3
b-foo1
c-foo0
d-foo2
这样吞吐量会提高很多,但是rowkey的读取耗费了一些开销
三.关于HBase中Rowkey设计的几点优化建议
1):尽可能的使列族的名称短一些 比如:private static final byte[] FAMILY_NAME = new byte[]{(byte)'c',(byte) 'f’}; 或者就一个字符 ‘c’
2):属性名称也尽可能的短一些 比如“RedisMonitorValue” 可以改为 rmv 达到实现约定或者读取的时候解析即可
3):RowKey的长度不要太长,好不好超过16字节 ,可以采用枚举或者字典值定义标识,使用long类型来表示,尽量使用编码压缩
4):好一个表只有一个列族,因为HBase对于两个或者三个以上的列族的处理不太好,并且跨列族之间的访问很耗费性能[]
5):创建表的时候,将列压缩[Compression.Algorithm.SNAPPY]
参考资料:
https://hbase.apache.org/book.html相关文章