HBase 多級索引

華爲方案html

華爲在HBTC 2012上由其高級技術經理Anoop Sam John透露了其二級索引方案,這在業界引發極大的反響,甚至有人認爲,若是華爲早點公佈這個方案,hbase的某些問題早就解決了。其核心思想是保證索引表和主表在同一個region server上。git

更新:目前該方案華爲已經開源,詳見:https://github.com/Huawei-Hadoop/hindexgithub

下面來對其方案作一個分析。apache

1.總體架構架構

這個架構在Client Ext中設定索引細節,在Balancer中收集信息,在Coprocessor中管理二級索引數據。ide

architecture  華爲hbase二級索引(secondary index)細節分析

2.表建立oop

在建立表的時候,在同一個region server上建立索引表,且一一對應。性能

tableCreate 華爲hbase二級索引(secondary index)細節分析

3.插入操做spa

在主表中插入某條數據後,用Coprocessor將索引列寫到索引表中去,寫道索引表中的數據的主鍵爲:region開始key+索引名+索引列值+主表row key。這麼作,是爲了讓其在同一個分佈規則下,索引表會跟主表在經過region server上,在查詢的時候就能夠少一次rpc。code

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);
  }

}
相關文章
相關標籤/搜索