转自: https://blog.csdn.net/finad01/article/details/45952781
------------------------------------------------------------------------------------------
hbase数据加盐(Salting)存储与协处理器查询数据的方法
用HBase存储数据时,如果不加任何处理,用户数据往往会集中在几个region中,从而导致数据处理的性能问题,写性能会不断下降,同时用MR处理时,往往会导致个别map处理非常耗时,下面主要介绍一种能够兼顾读写性能的hbase数据存储方法。
在HBase中,表的数据按Region存储,每个Region有StartKey,EndKey,默认情况下新建一个表只有一个region,然后随着不断写入数据,数据越来越多,region的size越来越大时,大到一定的阀值时Region会进行split成两个region,依次不断增加。这种默认方式,缺点主要是写的性能不断下降,数据主要集中在几个region中,同时region会经常split,当regionsplit的时候会导致regionserver的停顿,造成性能问题。
我们在实际使用中,用HBase存储的数据主要有用作两方面,一个是作为数据中心(历史数据备份库),提供查询接口供数据用户查询,另一个是用MR进行处理,统计一些有价值的数据。这样在MR进行处理的时候就会非常慢(我们的实际情况是几个小时,数据量在1亿),map慢的不能忍受。
后面我们采取了预分区的方法,比如建表的时候默认指定100个region,但还是没有解决数据集中的问题,因为我们的大部分数据是按时间作为rowkey的开头,比如20150524002300_1232,大部分数据还是集中在几个region中,其他region基本为空。
而我们想要的效果应该是让每个节点提供的请求处理都是均等的,同时数据能够相对均匀的分布到各个region中。为此我们最后采取的方法是数据加盐(salting)存储与hbase协处理器查询数据。
先介绍一下Hbase加盐存储,思路比较简单,每个region预分区都会指定一个startkey与endkey,然后插入数据的时候对rowkey进行hash取余,产生的code为盐值,添加到rowkey前面作为rowkey的组成部分。比如,我们预分区指定1000个region,每个region的startkey与endkey为000~999依次增加,region1:000-001,region2:001-002,....region1000:999-。然后插入数据rowkey="20150524002300_1232",
intsplitsCount= 1000;
StringrowKey= "20150524002300_1232";
int saltingCode = rowKey.hashCode()%splitsCount;
StringsaltingKey= ""+ saltingCode;
if(saltingCode < 10)
{
saltingKey = "00" + saltingKey;
}
else if(saltingCode < 100)
{
saltingKey = "0" + saltingKey;
}
rowKey = saltingKey + rowKey;
当然盐值的差生方法有很多,只要达到我们想要的效果即可。
这样就会使插入的数据相对均匀的分布到1000个region中去,然后MR程序进行处理时,每个region默认一个map处理,相对处理速度会有很大的提升,我们之前跑几个小时的map任务采用该方法后,只需要20分钟左右,效果还是非常明显的。
上面讲了存储,现在在讲一下怎么查询数据,由于插入的数据被我们默认都添加了盐值,导致本来在hbase连续存储的数据被分散到了多个region中,所以无论是根据rowkey查询单条记录,还是由startkey与endkey进行查询,都不能再简单的调用hbase接口进行查询,解决的方法是采用hbase协处理器进行查询,hbase协处理器包括两种,一种是观察者(Observer),另外一种是终端(Endpoint),我们这里需要使用的是后一种endpoint,基本思路是endpoint类似于关系型数据库中的存储过程,作用于每个region,每个region分别加盐查询,讲解过返回到客户端,客户端进行合并,就是最后的查询结果,比如我们查询"201501010000"与"20150524000000"之间的数据,region1查询"000201501010000"与"00020150524000000",region2查询"001201501010000"与"00120150524000000"... 最后1000个region均返回结果,进行合并就是我们要查询的结果。相应的具体实现后面文章给出。