Hbase多列范围查找(效率)

 

Hbase索引表的结构

Hbase Rowkey 设计

Hbase Filter

Hbase二级索引

 

Hbase索引表的结构

  在HBase中,表格的Rowkey按照字典排序,Region按照RowKey设置split point进行shard,通过这种方式实现的全局、分布式索引,成为了其成功的最大的砝码

  每一个索引建立一个表,然后依靠表的row key来实现范围检索。row key在HBase中是以B+ tree结构化有序存储的,所以scan起来会比较效率。
单表以row key存储索引,column value存储id值或其他数据 ,这就是Hbase索引表的结构。

  Hbase QualifierFilter用于过滤qualifier,也就是一个列族里面data:xxx,冒号后面的字符串

Hbase Rowkey 设计

  大数据最好从rowkey入手,ColumnValueFilter的数度是很慢的,hbase查询速度还是要依靠rowkey,所以根据业务逻辑把rowkey设计好,之后所有的查询都通过rowkey,是会非常快。 批量查询最好是用 scan的startkey endkey来做查询条件

  rowkey是hbase中很重要的一个设计,如果你把它当成普通字段那你的设计就有点失败了。它的设计可以说是一门艺术。你的查询如果不能把rowkey加入进来,那你的设计基本是失败的。加上rowkey,hbase可以快速地定位到具体的region去取你要的数据,否则就会满上遍野的找数据。

设计原则:

1. 长度越短越好

  Rowkey是一个二进制码流,Rowkey的长度被很多开发者建议说设计在10~100个字节,不过建议是越短越好,不要超过16个字节。

  原因如下:

  (1)数据的持久化文件HFile中是按照KeyValue存储的,如果Rowkey过长比如100个字节,1000万列数据光Rowkey就要占用100*1000万=10亿个字节,将近1G数据,这会极大影响HFile的存储效率;

  (2)MemStore将缓存部分数据到内存,如果Rowkey字段过长内存的有效利用率会降低,系统将无法缓存更多的数据,这会降低检索效率。因此Rowkey的字节长度越短越好。

  (3)目前操作系统是都是64位系统,内存8字节对齐。控制在16个字节,8字节的整数倍利用操作系统的最佳特性。

2. 散列原则:如果Rowkey是按时间戳的方式递增,不要将时间放在二进制码的前面,建议将Rowkey的高位作为散列字段,由程序循环生成,低位放时间字段,这样将提高数据均衡分布在每个Regionserver实现负载均衡的几率。如果没有散列字段,首字段直接是时间信息将产生所有新数据都在一个 RegionServer上堆积的热点现象,这样在做数据检索的时候负载将会集中在个别RegionServer,降低查询效率。

3. 唯一性

 

HBase按指定的条件获取一批记录时,使用的就是scan方法。 scan方法有以下特点:

(1)scan可以通过setCaching与setBatch方法提高速度(以空间换时间);

(2)scan可以通过setStartRow与setEndRow来限定范围。范围越小,性能越高。

通过巧妙的RowKey设计使我们批量获取记录集合中的元素挨在一起(应该在同一个Region下),可以在遍历结果时获得很好的性能。

(3)scan可以通过setFilter方法添加过滤器,这也是分页、多条件查询的基础。

 

设计RowKey时可以这样做:采用 UserID + CreateTime + FileID组成RowKey。

需要注意以下几点:

(1)每条记录的RowKey,每个字段都需要填充到相同长度。假如预期我们最多有10万量级的用户,则userID应该统一填充至6位,如000001,000002…

(2)结尾添加全局唯一的FileID的用意也是使每个文件对应的记录全局唯一。避免当UserID与CreateTime相同时的两个不同文件记录相互覆盖。

RowKey存储上述文件记录,在HBase表中是下面的结构:

rowKey(userID 6 + time 8 + fileID 6) name category ….

00000120120902000001

Hbase Filter

应用实例

+ View Code

HBase(0.96以上版本)过滤器Filter详解及实例代码

 

Hbase二级索引

  HBase在0.92之后引入了coprocessors,提供了一系列的钩子,让我们能够轻易实现访问控制和二级索引的特性。下面简单介绍下两种coprocessors,第一种是Observers,它实际类似于触发器,第二种是Endpoint,它类似与存储过程。由于这里只用到了Observers,所以只介绍Observers,想要更详细的介绍请查阅(https://blogs.apache.org/hbase/entry/coprocessor_introduction)。observers分为三种:

RegionObserver:提供数据操作事件钩子;

WALObserver:提供WAL(write ahead log)相关操作事件钩子;

MasterObserver:提供DDL操作事件钩子。

 

在二级索引的实现技术上一般有几个方案:

1.      表索引

使用单独的hbase表存储索引数据,业务表的索引列值做为索引表的rowkey,业务表的rowkey做为索引表的qualifier或value。

问题:对数据更新性能影响较大;无法保证一致性;Client查询需要2次RPC(先索引表再数据表)。

2.      列索引

与业务表使用相同表,使用单独列族存储索引,用户数据列值做为索引列族的Qualifier,用户数据Qualifier做为索引列族的列值。适用于单行有上百万Qualifier的数据模型,如网盘应用中网盘ID做为rowkey,网盘的目录元数据都存储在一个hbase row内。(facebook消息模型也是此方案)

可保证事务性

 

为了实现像SQL一样检索数据,select * from table where col=val。针对HBase Secondary Indexing的方案,成为HBase新版本(0.96)呼声最高的一项Feature。

 

粗略分析了当前的技术,大概的方案可以总结为这样两类:

1、使用HBase的coprocessor。CoProcessor相当于HBase的Observer+hook,目前支持MasterObserver、RegionObserver和WALObserver,基本上对于HBase Table的管理、数据的Put、Delete、Get等操作都可以找到对应的pre***和post***。这样如果需要对于某一项Column建立Secondary Indexing,就可以在Put、Delete的时候,将其信息更新到另外一张索引表中。如图二所示,对于Indexing里面的value值是否存储的问题,可以根据需要进行控制,如果value的空间开销不大,逆向的检索又比较频繁,可以直接存储在Indexing Table中,反之则避免这种情况。HBase二级索引方案总结

图2 使用HBase Coprocessor实现Secondary Indexing

2、由客户端发起对于主表和索引表的Put、Delete操作的双重操作。源自:http://hadoop-hbase.blogspot.com/2012/10/musings-on-secondary-indexes.html 【墙外】

它具体的做法总结起来有:

  • 设置主表的TTL(Time To Live)比索引表小一点,让其略早一点消亡。

  • 不要在IndexingTable存储Value值,即删除如图2所示的val列。

  • Put操作时,对于操作的主表的所有列,使用同一的Local TimeStamp的值,更新到Indexing Table,然后使用该TimeStamp插入主表数据。

  • Delete操作时,首先操作主表的数据,然后再去更新Indexing Table的数据。

虽然在这种方案里无法保证原子性和一致性,但是通过TimeStamp的设置,No Locks和 No Server-side codes,使其在二级索引上有着较大的优势。至于中间出错的环节,我们看看是否可以容忍:

1)Put索引表成功,Put主表失败。由于Indexing Table不存储val值,仍需要跳转到Main Table,所以这样的错误相当于拿一个Stale index去访问对应Rowkey吧了,对结果正确性没有影响。

2)Delete主表成功,Delete索引表失败。都是索引表的内容>=主表的内容而已,而实际返回值需要通过主表进行。

 应用场景:

1、主表服务在线业务,它的性能需要保证。使用coprocessor和客户端的封装也好,都会影响其性能,所以在正常情况下,直接操作都不太合适。如果想使用方案二,我倒是感觉,可以调整Indexing Table的操作方式,去除保证其安全性的内容,比如可以关闭写HLOG,这样会进一步减低其操作的延迟。

2、离线更新索引表。在真正需要二级索引的场景内,其时效性要求往往不高。可以将索引实时更新到Redis等KV系统中,定时从KV更新索引到Hbase的Indexing Table中。PS:Redis里面有DB设置的概念,可以按照时间段进行隔离,这样某段时间内的数据会更新到Redis上,保证Redis导入MapReduce之后仍然可以进行update操作。

 

coprocessor代码实现  ??

 

+ View Code

首先在HBase-0.19.3中必须设置参数,使得Hbase可以使用索引,修改$HBASE_INSTALL_DIR/conf/hbase-site.xml:

+ View Code

(1)创建表时,增加二级索引:

+ View Code

(2)在已经存在的表中,增加索引

+ View Code

(3)删除存在的索引

+ View Code

(4)通过索引scan所有数据

+ View Code

 (5)通过索引scan一部分子集,通过ColumnValueFilter过滤。

  使用SingleColumnValueFilter会影响查询性能,在真正处理海量数据时会消耗很大的资源,且需要较长的时间

+ View Code

一般不建议用Filter,scan.setFilters(),通过filter设置的条件查不到数据时,响应速度非常慢,大概在十几秒,有时会超时,

但可以查到数据时,响应速度只有几百ms,差距非常大

1

2

3

4

5

6

7

8

9

10

Scan scan = new Scan();

FilterList filters = new FilterList();

 

for (String[] param : params)

{

//param[0]为列名,param[1]为相应的值

filters.addFilter(new SingleColumnValueFilter("INFO".getBytes(), param[0].getBytes(), CompareOp.EQUAL, param[1].getBytes()));

}

 

scan.setFilter(filters);

(6)一个完全的例子

+ View Code

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值