HBase rowkey设计案例

2022-05-06 19:34:13 浏览数 (1)

hbase所谓的三维有序存储的三维是指:rowkey(行主键),column key(columnFamily qualifier),timestamp(时间戳)三部分组成的三维有序存储。

rowkey是行的主键,而且hbase只能用个rowkey,或者一个rowkey范围即scan来查找数据。所以 rowkey的设计是至关重要的,关系到你应用层的查询效率。

rowkey是以字典顺序排序的,存储的是字节码。

根据rowkey范围查询的时候,一般是知道startRowkey,如果我们通过scan只传startRowKey : d开头的,那么查询的是所有比d大的都查了,而我们只需要d开头的数据,那就要通过endRowKey来限制。我们可以通过设定endRowKey为:d 开头,后面的根据你的rowkey组合来设定,一般是加比startKey大一位。比如说rowkey设计为:用户ID-日期,那么查某个用户某天的数 据,startKEY为3231-20121212,endKey为:3231 201213,那么你查到的就是用户为3231在20121212这一天 的数据。

需要访问的数据rowkey连续的话,scan到的有用数据比较多,利用率高,能够避免或减少反复内存倒换。

rowkey的设计和数据的分布有很大关系,rowkey设计的时候需要保证数据入库时的并发度,但又不能过于分散。

可枚举属性值较少的属性放在rowkey前面 在rowkey中,需要放入多个属性,这多个属性的先后次序和访问的效率有直接的关系。一个普遍的规则是:数量较少,可控的属性放在rowkey前面(如ServiceType,CPID等);反之放在后面(如url,mxid等)。这样做的原因是可控属性放在前面,对各种不同查询需求的平衡性强一些,反之平衡性较差。

案例1:

代码语言:javascript复制
201010-http-cp001-s-shanghai-xxx-1
201010-http-cp002-s-shenzhen-xxx-2
201010-rtsp-cp001-s-shanghai-xxx-1
201010-rtsp-cp002-s-shenzhen-xxx-2

ServiceType可枚举,并且数量较少,分为http和rtsp两种,放在前面; 而cpid可能会比较多(假设有5个cp),因此放在后面。 这样的设计能够适应如下两种需求,复杂度都比较小: 1) 查询2010年10月所有cp的http数据。这种需求设置scan的startrow=‘201010-http-’,endrow=‘201010-http-z’,即可。 2) 查询2010年10月cp001的所有协议的数据。这种需求下,根据scan rowkey连续的原则,需要将查询划分成两个scan,分别查询http类型cp001的数据和rtsp类型cp001的数据。

但是,如果将cp放在前面,如下所示,适应性就差一些,如下所示案例2:

代码语言:javascript复制
201010-cp001-http-s-shanghai-xxx-1
201010-cp002-http-s-shenzhen-xxx-2
201010-cp001-rtsp-s-shanghai-xxx-1
201010-cp002-rtsp-s-shenzhen-xxx-2

1) 查询2010年10月cp001的所有协议的数据。 这种需求下,设置scan的startrow=‘201010-cp001-’,endrow=‘201010-cp001-z’,即可。 2) 查询2010年10月,所有cp的http数据。 这种需求下,根据scan的rowkey连续原则,需要将查询分成cp001-http、cp002-http、cp003-http、cp004-http、cp005-http五个查询进行,相对模型一复杂了一些。

业务访问中权重高的key放在前面

例如URLRecords表的主要用途是用来计算当天的URL访问排名。根据业务需求,需要访问某天的所有URL,因此date是主键,权重更高,放在前面,而URL则放在后面。

构造冗余数据 例如,percontent的数据包含了URL Records的数据,URL Records的数据是冗余存储的,区别在于percontent的URL放在date前面,而URL Records表的URL放在date后面。这就是由于URL在满足不同需求的时候,权重不同,由于URL Records需要的数据量不大,因此采用冗余的机制解决该矛盾。 权衡需求的重要性和系统忍受度选择一种方案 当两种需求有矛盾,但其中一方属于次要需求,并且在系统忍受度范围之内的话,可以舍弃一种方案。优先满足需求更强的一方

时间属性在rowkey中的使用 如果需要经常访问特定时间段的数据,将时间属性放在rowkey中是一个较好的选择。 和利用时间戳来访问特定时间段的数据方法相比,将时间属性放在rowkey中具有可控性,容易将能够同时访问的数据相对集中存放的优点。 时间属性放在rowkey中需要注意数据分布和并发度的问题:hbase数据是按照rowkey排序的,时间属性放在rowkey中容易造成数据总是在末尾写入的情况,这种情况下并发度很差。这种情况可以通过在时间属性前面增加prefix和提前预分region的方法解决。

循环key使用 (1)存在问题 如果rowkey中有时间属性,并且随着时间的增加,rowkey会不断的增大下去的话,会造成region数量不断地增加。如果使用TTL来控制数据的生命周期,一些老的数据就会过期,进而导致老的region数据量会逐渐减少甚至成为空的region。这样一方面region总数在不断增加,另外一方面老的region在不断的成为空的region,而空的region不会自动合并,进而造成过多空的region占用负载和内存消耗的情况。 (2)解决办法 这种情况下,可以使用循环key的方法来解决。思路是根据数据的生命周期设定rowkey的循环周期,当一个周期过去以后,通过时间映射的方法,继续使用老的过期数据的rowkey。 例如,key的格式如下: YY-MM-DD-URL。如果数据的生命周期是一年,则可以使用MM-DD-URL的格式。这样当前一年过去以后,数据已经老化,后一年的数据可以继续写入前一年的位置,使用前一年数据的rowkey。这样可以避免空的region占用资源的情况。

根据hbase的原理,key的周期需要至少比TTL大2* hbase.hregion.majorcompaction(默认24小时)的时间,才能够保证过期的数据能够在key循环回来之前得到完全清理。 按照时间周期进行建表的方式也可以解决空region的问题,和循环key方法相比较,循环key的优点如下:

  • 操作简单,不需要重复建表,系统自动处理

同样,循环key具有如下劣势:

  • 需要使用TTL来老化数据,可能会增加compact负担
  • 需要保证查询操作不会查询到过期数据,否则会影响系统性能。

如果在系统压力不是特别大,需要长期运行,能够控制查询不会查询到过期数据的场景下,建议使用TTL 循环key的方式,否则建议使用按照时间周期进行建表的方式。

通过rowkey设计来控制并发度 在相同业务模式下,不同的rowkey设计系统的并发度不一样。和按天建表的思路类似,通过rowkey控制并发度的原则是激活的region总数适中,每个regionserver的激活Region数大于1,小于(写操作内存/flushsize)为宜。

为了实现这一点,可以将可枚举、数量有限的属性放在rowkey的前面,时间放在后面的方式来提高并发度;通过将大粒度的时间属性(如天、小时等)放在rowkey前面,数量很大的可枚举属性(如电话号码、URL等)放在后面的方法来控制激活的region数。

0 人点赞