HBase建立二级索引的一些解决方案(Solr+hbase方案等)

283 阅读6分钟

HBase的一级索引就是rowkey,我们只能通过rowkey进行检索。如果我们相对hbase里面列族的列列进行一些组合查询,就需要采用HBase的二级索引方案来进行多条件的查询。


HBase建立二级索引的一些解决方案
//-------------------------------------------------------------------
常见的二级索引方案有以下几种: 
1.MapReduce方案 
2.ITHBASE方案 
3.IHBASE方案 
4.Coprocessor方案 
5.Solr+hbase方案




MapReduce方案


IndexBuilder:利用MR的方式构建Index 
优点:可以并发批量构建Index 
缺点:当对hbase插入一条数据时,不能实时构建Index


举例: 


原表:


row  1      f1:name  zhangsan
row  2      f1:name  lisi
row  3      f1:name  wangwu


有三个学生号123,要去查看张三这个人的学号是多少时,若数据量非常大时,全表扫描不太现实,便想怎么样构建一个索引表,如下:创建一个反向索引表123,把张三作为rowkey,学号为列,就可通过rowkey张三查到这个记录。通过反向索引可以对某些列进行查询。


索引表:


row     zhangsan    f1:id   1
row     lisi        f1:id   2
row     wangwu      f1:id   3




Demo:执行这个程序 $ hbase IndexBuilder 参数  ()
\

package IndexDouble;


import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;


import org.apache.commons.collections.map.HashedMap;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.HConnection;
import org.apache.hadoop.hbase.client.HConnectionManager;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.MultiTableOutputFormat;
import org.apache.hadoop.hbase.mapreduce.TableInputFormat;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.hbase.mapreduce.TableMapper;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.util.GenericOptionsParser;




public class IndexBuilder {
    private String rootDir;
    private String zkServer;
    private String port;
    private Configuration conf; 
    private HConnection hConn = null;


    private IndexBuilder(String rootDir,String zkServer,String port) throws IOException{
        this.rootDir = rootDir;
        this.zkServer = zkServer;
        this.port = port;


        conf = HBaseConfiguration.create();
        conf.set("hbase.rootdir", rootDir);
        conf.set("hbase.zookeeper.quorum", zkServer);
        conf.set("hbase.zookeeper.property.clientPort", port);


        hConn = HConnectionManager.createConnection(conf);  
    }


    static class MyMapper extends TableMapper<ImmutableBytesWritable, Put>{


        //记录了要进行索引的列
        private Map<byte[], ImmutableBytesWritable> indexes = new HashMap<byte[], ImmutableBytesWritable>();


        private String familyName;


        @Override
        protected void map(ImmutableBytesWritable key, Result value,
                Context context) throws IOException, InterruptedException {
            //原始表列
            Set<byte[]> keys = indexes.keySet();


            //索引表的rowkey是原始表的列,索引表的列是原始表的rowkey


            for (byte[] k : keys){


                //获得新建索引表的表名
                ImmutableBytesWritable indexTableName = indexes.get(k);


                //Result存放的是原始表的数据
                //查找到内容             根据列族 和 列 得到原始表的值
                byte[] val = value.getValue(Bytes.toBytes(familyName), k);


                if (val != null) {
                    //索引表
                    Put put = new Put(val);//索引表行键
                    //列族  列   原始表的行键
                    put.add(Bytes.toBytes("f1"),Bytes.toBytes("id"),key.get());
                    context.write(indexTableName, put);
                }
            }


        }


        //真正运行Map之前执行一些处理。
        @Override
        protected void setup(Context context) throws IOException,
                InterruptedException {
            //通过上下文得到配置
            Configuration conf = context.getConfiguration();


            //获得表名
            String tableName = conf.get("tableName"); 
            //String family = conf.get("familyName");
            //获得列族
            familyName = conf.get("columnFamily");


            //获得列
            String[] qualifiers = conf.getStrings("qualifiers"); 


            for (String qualifier : qualifiers) {
                //建立一个映射,为每一个列创建一个表,表的名字tableName+"-"+qualifier
                //原始表的列    索引表新建表名
                indexes.put(Bytes.toBytes(qualifier), 
                        new ImmutableBytesWritable(Bytes.toBytes(tableName+"-"+qualifier)));
            }


        }   
    }




    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {


        String rootDir = "hdfs://hadoop1:8020/hbase";
        String zkServer = "hadoop1";
        String port = "2181";


        IndexBuilder conn = new IndexBuilder(rootDir,zkServer,port);


        String[] otherArgs = new GenericOptionsParser(conn.conf, args).getRemainingArgs(); 




        //至少传三个参数IndexBuilder: TableName,ColumnFamily,Qualifier
        if(otherArgs.length<3){
            System.exit(-1);
        }
        //表名
        String tableName = otherArgs[0];
        //列族
        String columnFamily = otherArgs[1];


        conn.conf.set("tableName", tableName);
        conn.conf.set("columnFamily", columnFamily);


        //列  可能存在多个列
        String[] qualifiers = new String[otherArgs.length-2];


        for (int i = 0; i < qualifiers.length; i++) {
            qualifiers[i] = otherArgs[i+2];
        }


        //设置列
        conn.conf.setStrings("qualifiers", qualifiers);


        @SuppressWarnings("deprecation")
        Job job = new Job(conn.conf,tableName);


        job.setJarByClass(IndexBuilder.class);


        job.setMapperClass(MyMapper.class);
        job.setNumReduceTasks(0);//由于不需要执行reduce阶段


        job.setInputFormatClass(TableInputFormat.class);
        job.setOutputFormatClass(MultiTableOutputFormat.class);


        Scan scan = new Scan();
        scan.setCaching(1000);//一次性批量读取多少条记录


        //初始化MR
        TableMapReduceUtil.initTableMapperJob(tableName,scan,  MyMapper.class, ImmutableBytesWritable.class, Put.class, job);


        job.waitForCompletion(true);


    }
}




创建原始表
hbase(main):002:0> create 'studentinfo','f1'
0 row(s) in 0.6520 seconds


=> Hbase::Table - studentinfo



hbase(main):003:0> put 'studentinfo','1','f1:name','zhangsan'
//0 row(s) in 0.1640 seconds


hbase(main):004:0> put 'studentinfo','2','f1:name','lisi'
//0 row(s) in 0.0240 seconds


hbase(main):005:0> put 'studentinfo','3','f1:name','wangwu'
//0 row(s) in 0.0290 seconds


hbase(main):006:0> scan 'studentinfo'
ROW                      COLUMN+CELL
1                       column=f1:name, timestamp=1436262175823, value=zhangsan
2                       column=f1:name, timestamp=1436262183922, value=lisi
3                       column=f1:name, timestamp=1436262189250, value=wangwu
//3 row(s) in 0.0530 seconds


创建索引表

hbase(main):007:0> create 'studentinfo-name','f1'
//0 row(s) in 0.7740 seconds


=> Hbase::Table - studentinfo-name


执行结果
> scan 'studentinfo-name'
\




ITHBASE方案

//------------------------------------------------------------------------------
优点:ITHBase(Indexed Transactional HBase)是HBase的一个事物型的带索引的扩展。 
缺点:需要重构hbase,几年没有更新。 
github.com/hbase-trx/h…




IHBASE方案
//------------------------------------------------------------------------------
**优点:**IHBase(Indexed HBase)是HBase的一个扩展,用干支持更快的扫描。 
缺点:需要重构hbase,版本老旧。 
原理:在Memstore满了以后刷磁盘时,IHBase会进行拦截请求,并为这个memstore的数据构建索引,索引另一个CF的方式存储在表内。scan的时候,IHBase会结合索引列中的标记,来加速scan。 
github.com/ykulbak/ihb…




Coprocessor方案
//------------------------------------------------------------------------------
HIndex–来自华为的HBase二级索引 
github.com/Huawei-Hado…


The solution is 100% Java, compatible with Apache HBase 0.94.8, and is open sourced under ASL.


Following capabilities are supported currently. 
1.multiple indexes on table, 
2.multi column index, 
3.index based on part of a column value, 
4.equals and range condition scans using index, and 
5.bulk loading data to indexed table (Indexing done with bulk load).




Solr+hbase方案
//------------------------------------------------------------------------------
Solr是一个独立的企业级搜索应用服务器,它对并提供类似干Web-service的API接口。用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引;也可以通过Http Get操作提出查找请求,并得到XML格式的返回结果。 
Solr是一个高性能,采用Java5开发,基干Lucene的全文搜索服务器。同时对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展并对查询性能进行了优化,并且提供了一个完善的功能节理界面,是一款非常优秀的全文搜索引擎。

HBase无可置疑拥有其优势,但其本身只对rowkey支持毫秒级的快速检索,对于多字段的组合查询却无能为力。 
基于Solr的HBase多条件查询原理很简单,将HBase表中涉及条件过滤的字段和rowkey在Solr中建立索引,通过Solr的多条件查询快速获得符合过滤条件的rowkey值,拿到这些rowkey之后在HBASE中通过指定rowkey进行查询。 \

\

本文转载自:blog.csdn.net/scgaliguodo…

\

\

关于使用hbase进行多维度条件实时查询的方案调研。
1.MapReduce方案  
优点:并发批量构建Index 
缺点:不能实时构建Index  
2.ITHBASE方案 
缺点:需要重构hbase,几年没有更新。 
3.IHBASE方案 
缺点:需要重构hbase。 
4.Coprocessor方案 
华为的HBase二级索引采用此方案(hindex 代码开源)。
1)、索引和数据分别放在不同表里;
2)、所有的运算逻辑全都放在服务端;
3)、需要修改HBase源码,侵入性大
4)、 查询时无需指定,即可自动使用最优索引
缺点:代码很复杂,代码量非常多。一下子要弄明白原理可能比较困难。hindex和公司的HBase版本不兼容性
5.Solr+hbase方案
缺点:对Solr不熟悉 
6.CCIndex   
缺点: 如存储开销比较大,尤其是当索引列比较多的时候,空间开销会更大;索引更新代价比较高,会影响系统的吞吐量;索引创建以后,不能够动态增加或修改。
7.360的hbase二级索引
360二级索引的特点如下:
1)、索引和Rowkey在同一个表里;
2)、支持多范围与操作优化;
3)、支持索引重建
缺点:没有开源,需要按照他的思想去实现,原理不是太清楚,只明白一点点,按照这个思想来重新搭建也可能非常耗时间。
8.phoenix的二级索引
好处:开源,自带二级索引。
现状:公司的hbase集群,资源有限。目前主要是提供给dmp在使用。刚好能撑住目前的服务。
偶尔有压力的时候,还会挂掉几台机器。
按照目前的需求,只有两条方案:一个是按照上面的思想自己开发一个hbase的二级索引工具,另外一个是使用phoenix自带二级索引。
依照目前的hbase的集群使用情况,就算自己开发出来了二级索引,估计集群资源不够用的前提下,也发挥不出二级索引的速度优势。
所以只能暂且在phoenix的现有资源上优化我们的程序性能,尽量减少检索时间。    

\




\