華爲方案html
華爲在HBTC 2012上由其高級技術經理Anoop Sam John透露了其二級索引方案,這在業界引發極大的反響,甚至有人認爲,若是華爲早點公佈這個方案,hbase的某些問題早就解決了。其核心思想是保證索引表和主表在同一個region server上。git
更新:目前該方案華爲已經開源,詳見:https://github.com/Huawei-Hadoop/hindexgithub
下面來對其方案作一個分析。apache
1.總體架構架構
這個架構在Client Ext中設定索引細節,在Balancer中收集信息,在Coprocessor中管理二級索引數據。ide
2.表建立oop
在建立表的時候,在同一個region server上建立索引表,且一一對應。性能
3.插入操做spa
在主表中插入某條數據後,用Coprocessor將索引列寫到索引表中去,寫道索引表中的數據的主鍵爲:region開始key+索引名+索引列值+主表row key。這麼作,是爲了讓其在同一個分佈規則下,索引表會跟主表在經過region server上,在查詢的時候就能夠少一次rpc。code
4.scan操做
一個查詢到來的時候,經過coprocessor鉤子,先從索引表中查詢範圍row,而後再從主表中相關row中掃描得到最終數據。
5. split操做處理
爲了使主表和索引表在同一個RS上,要禁用索引表的自動和手動split,只能由主表split的時候觸發,當主表split的時候,對索引表按其對應數據進行劃分,同時,對索引表的第二個daughter split的row key的前面部分修改成對應的主鍵的row key。
6. 性能
查詢性能極大提高,插入性能降低10%左右
總結,本文對華爲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); } }