HBase 多级索引

华为方案

华为在HBTC 2012上由其高级技术经理Anoop Sam John透露了其二级索引方案,这在业界引起极大的反响,甚至有人认为,如果华为早点公布这个方案,hbase的某些问题早就解决了。其核心思想是保证索引表和主表在同一个region server上。

更新:目前该方案华为已经开源,详见:https://github.com/Huawei-Hadoop/hindex

下面来对其方案做一个分析。

1.整体架构

这个架构在Client Ext中设定索引细节,在Balancer中收集信息,在Coprocessor中管理二级索引数据。

architecture  华为hbase二级索引(secondary index)细节分析

2.表创建

在创建表的时候,在同一个region server上创建索引表,且一一对应。

tableCreate 华为hbase二级索引(secondary index)细节分析

3.插入操作

在主表中插入某条数据后,用Coprocessor将索引列写到索引表中去,写道索引表中的数据的主键为:region开始key+索引名+索引列值+主表row key。这么做,是为了让其在同一个分布规则下,索引表会跟主表在通过region server上,在查询的时候就可以少一次rpc。

putOperation 华为hbase二级索引(secondary index)细节分析

4.scan操作

一个查询到来的时候,通过coprocessor钩子,先从索引表中查询范围row,然后再从主表中相关row中扫描获得最终数据。

scan 华为hbase二级索引(secondary index)细节分析

5. split操作处理

为了使主表和索引表在同一个RS上,要禁用索引表的自动和手动split,只能由主表split的时候触发,当主表split的时候,对索引表按其对应数据进行划分,同时,对索引表的第二个daughter split的row key的前面部分修改为对应的主键的row key。

split 华为hbase二级索引(secondary index)细节分析

6. 性能

查询性能极大提升,插入性能下降10%左右

performance1 华为hbase二级索引(secondary index)细节分析

performance2 华为hbase二级索引(secondary index)细节分析

总结,本文对华为hbase使用coprocessor进行二级索引的方案的创建表,插入数据,查询数据的步骤进行了一个粗略分析,以窥其全貌。在使用的时候,可以作为一个参考。

 

该方案主要优势

  1. Idx表的索引所处位置与原数据位置处于同一个Region内

  2. 使用RegionObserver钩子,减少了IPC次数

 HBase官方文档:

      http://hbase.apache.org/book.html#cp

      https://blogs.apache.org/hbase/entry/coprocessor_introduction

相关部署:

HBase官方example :

  alter 't1', METHOD => 'table_att',
    'coprocessor'=>'hdfs:///foo.jar|com.foo.FooRegionObserver|1001|arg1=1,arg2=2'

上传jar包到hdfs:

  bin/hdfs dfs -put ~/bzhou/wad-hbase-0.0.1-SNAPSHOT.jar /data/weidou_ad/wad-hbase.jar

上传后的位置:

  hdfs://wdc0:9000/data/weidou_ad/wad-hbase.jar

修改HBase表:

  alter 'TestCoprocessor', METHOD => 'table_att', 
    'coprocessor'=>'hdfs://wdc0:9000/data/weidou_ad/wad-hbase.jar|com.weidou.wad.hbase.WadBeforeScan||'

 

相关开发:

class XXXXX implements RegionObserver

public class WadBeforeScan extends BaseRegionObserver {

  Log log = LogFactory.getLog(WadBeforeScan.class);

  @Override
  public void preGetOp(ObserverContext<RegionCoprocessorEnvironment> e, Get get, List<Cell> results)
      throws IOException {
    super.preGetOp(e, get, results);
  }

  @Override
  public boolean preScannerNext(ObserverContext<RegionCoprocessorEnvironment> e, InternalScanner s,
      List<Result> results, int limit, boolean hasMore) throws IOException {
    log.info("preScannerNext.limit:" + limit);
    log.info("preScannerNext.hasMore:" + hasMore);
    log.info("preScannerNext.result.size:" + ((results == null) ? 0 : results.size()));
//    if (results != null) {
//      int idx = 0;
//      for (Result r : results) {
//        log.info("preScannerNext.result." + (++idx) + ":" + r.toString());
//      }
//    }
//
//    HTableInterface itf = e.getEnvironment().getTable(TableName.valueOf("BidRequest"));
//
//    Scan scan = new Scan();
//    byte[] startRow = Bytes.toBytes("0_00035792-a7ea-478e-8c7d-4ce9015fe7e9");
//    byte[] endRow = Bytes.toBytes("0_00035792-a7ea-478e-8c7d-4ce9015fe7e9");
//    scan.setStartRow(startRow);
//    scan.setStartRow(endRow);
//
//    ResultScanner resultscanner = itf.getScanner(scan);
//    for (Result result : resultscanner) {
//      results.add(result);
//    }

     String rowkey1 = "testrowkey1";
     String family1 = "testfamily1";
     String column1 = "testcol1";
     String value1 = "testvalue1";
    
     Cell cell =
     new KeyValue(Bytes.toBytes(rowkey1), Bytes.toBytes(family1), Bytes.toBytes(column1),
     Bytes.toBytes(value1));
     List<Cell> cells = Lists.newArrayList();
     cells.add(cell);
     Result r = Result.create(cells);
     results.add(r);
    
    return super.preScannerNext(e, s, results, limit, hasMore);
  }

}

 

posted @ 2015-06-03 16:36  njuzhoubing  阅读(3008)  评论(0编辑  收藏  举报