一、Solr+hbase方案
Solr是一个独立的企业级搜索应用server,它对并提供相似干Web-service的API接口。用户能够通过http请求,向搜索引擎server提交一定格式的XML文件,生成索引。也能够通过Http Get操作提出查找请求,并得到XML格式的返回结果。
Solr是一个高性能。采用Java5开发。基干Lucene的全文搜索server。同一时候对其进行了扩展。提供了比Lucene更为丰富的查询语言,同一时候实现了可配置、可扩展并对查询性能进行了优化,而且提供了一个完好的功能节理界面。是一款非常优秀的全文搜索引擎。
HBase无可置疑拥有其优势,但其本身仅仅对rowkey支持毫秒级的高速检索,对于多字段的组合查询却无能为力。
基于Solr的HBase多条件查询原理非常easy。将HBase表中涉及条件过滤的字段和rowkey在Solr中建立索引,通过Solr的多条件查询高速获得符合过滤条件的rowkey值,拿到这些rowkey之后在HBASE中通过指定rowkey进行查询。
使用Solr+Hbase-solr(Hbase-indexer)配置实现HBase二级索引 https://www.jianshu.com/p/a7e2079ded33
Solr+Hbase+Hbase Indexer查询方案流程整合
原文:
测试环境:
solr 4.0.0版本,使用其自带的jetty服务端容器,单节点;
hbase-0.94.2-cdh4.2.1,10台Lunux服务器组成的HBase集群。
HBase中2512万条数据172个字段;
Solr索引HBase中的100万条数据;
测试结果:
1、100万条数据在Solr中对8个字段建立索引。在Solr中最多8个过滤条件获取51316条数据的rowkey值,基本在57-80毫秒。
根据Solr返回的rowkey值在HBase表中获取所有51316条数据12个字段值,耗时基本在15秒;
2、数据量同上,过滤条件同上,采用Solr分页查询,每次获取20条数据,Solr获得20个rowkey值耗时4-10毫秒,拿到Solr传入的rowkey值在HBase中获取对应20条12个字段的数据,耗时6毫秒。
一、Hbase建表
Hbase表需要开启REPLICATION复制功能
create 'table',{NAME => 'info', REPLICATION_SCOPE => 1}
其中1表示开启replication功能,0表示不开启,默认为0
对于已经创建的表可以使用如下命令
disable 'table' alter 'table',{NAME => 'info', REPLICATION_SCOPE => 1}
enable 'table'
二、Solr 部分
1、生成实体配置文件
/testhbase/cdhsolr/testIndex是自定义路径,可以自己设置
solrctl instancedir --generate /testhbase/cdhsolr/testIndex
1. 创建SolrCloud
使用如下指令创建SolrCloud,生成配置文件:
solrctl --zk master:2181,slave1:2181,slave2:2181/solr instancedir \
--generate ~/solr/myConfig
其中–zk参数为zookeeper集群的结点:端口号,以逗号分隔。
该指令会在~/solr/myConfig
目录下生成conf
文件夹,其中包含配置schema.xml文件。
2、修改Solr的配置文件schema.xml
添加我们需要索引的多个字段(配置文件位于“/opt/apache-solr-4.0.0/example/solr/conf/”)
<field name="COL1" type="string" indexed="true" stored="true" multiValued="false" required="true"/>
<field name="COL2" type="string" indexed="true" stored="true" multiValued="false" required="true"/>
<field name="rowkey" type="string" indexed="true" stored="true" required="true" multiValued="false" />
<field name="time" type="string" indexed="true" stored="true" required="false" multiValued="false" />
<field name="tebid" type="string" indexed="true" stored="true" required="false" multiValued="false" />
<field name="tetid" type="string" indexed="true" stored="true" required="false" multiValued="false" />
<field name="puid" type="string" indexed="true" stored="true" required="false" multiValued="false" />
<field name="mgcvid" type="string" indexed="true" stored="true" required="false" multiValued="false" />
<field name="mtcvid" type="string" indexed="true" stored="true" required="false" multiValued="false" />
<field name="smaid" type="string" indexed="true" stored="true" required="false" multiValued="false" />
<field name="mtlkid" type="string" indexed="true" stored="true" required="false" multiValued="false" />
另外关键的一点是修改原有的uniqueKey,本文设置HBase表的rowkey字段为Solr索引的uniqueKey:
<uniqueKey>rowkey</uniqueKey>
name:属性的名称,这里有个特殊的属性“version”是必须添加的。
type:字段的数据结构类型,所用到的类型需要在fieldType中设置。我这里将type全部设置为string是为了避免异常类型的数据 导致索引建立失败,正常情况下应该根据实际字段类型设置,比如整型字段设置为int,更加有利于索引的建立和检索;
indexed:是否创建索引。建议不参与条件过滤的字段一律设置为false;
stored:是否存储原始数据。 建议根据实际需求只将需要获取值的字段设置为true,以免浪费存储,比如我们的场景只需要获取 rowkey,那么只需把rowkey字段设置为true即可,其他字段全部设置flase;
required:添加文档时,该字段必须存在,类似mysql的not null。如果数据源某个字段可能存在空值,那么此属性必需设置为 false,不然Solr会抛出异常;
multiValued 参数代表此字段是否允许有多个值,通常都设置为false,根据实际需求可设置为true。
也可修改solrconfig.xml 文件中autoCommit. 这里还可以调整同步间隔,比如把60000改成120000.
<autoCommit>
<maxTime>${solr.autoCommit.maxTime:60000}</maxTime>
<openSearcher>true</openSearcher>
</autoCommit>
添加拼音分词
添加 pinyin4j-2.5.0.jar lucene-analyzers-smartcn-4.10.3.jar 到/opt/cloudera/parcels/CDH/lib/solr/webapps/solr/WEB-INF/lib下。分发到其它从节点
修改schemal.xml
<fieldType name="text_pinyin" class="solr.TextField" positionIncrementGap="0">
<analyzer type="index">
<tokenizer class="org.apache.lucene.analysis.cn.smart.SmartChineseSentenceTokenizerFactory"/>
<filter class="org.apache.lucene.analysis.cn.smart.SmartChineseWordTokenFilterFactory"/>
<filter class="com.shentong.search.analyzers.PinyinTransformTokenFilterFactory" minTermLenght="2" />
<filter class="com.shentong.search.analyzers.PinyinNGramTokenFilterFactory" minGram="1" maxGram="20" />
</analyzer>
<analyzer type="query">
<tokenizer class="org.apache.lucene.analysis.cn.smart.SmartChineseSentenceTokenizerFactory"/>
<filter class="org.apache.lucene.analysis.cn.smart.SmartChineseWordTokenFilterFactory"/>
<filter class="com.shentong.search.analyzers.PinyinTransformTokenFilterFactory" minTermLenght="2" />
<filter class="com.shentong.search.analyzers.PinyinNGramTokenFilterFactory" minGram="1" maxGram="20" />
</analyzer>
</fieldType>
添加smartcn分词
<fieldType name="text_smartcn" class="solr.TextField" positionIncrementGap="0">
<analyzer type="index">
<tokenizer class="org.apache.lucene.analysis.cn.smart.SmartChineseSentenceTokenizerFactory"/>
<filter class="org.apache.lucene.analysis.cn.smart.SmartChineseWordTokenFilterFactory"/>
</analyzer>
<analyzer type="query">
<tokenizer class="org.apache.lucene.analysis.cn.smart.SmartChineseSentenceTokenizerFactory"/>
<filter class="org.apache.lucene.analysis.cn.smart.SmartChineseWordTokenFilterFactory"/>
</analyzer>
</fieldType>
重启solr集群
3、 初始化Collection
创建collection实例并配置文件上传到zookeeper,命令
solrctl instancedir --create testIndex /testhbase/cdhsolr/testIndex
使用以下指令初始化Collection,该Collection名为testCollection,并将以上修改的配置文件所在的文件夹上传至zookeeper集群进行管理。
solrctl --zk master:2181,slave1:2181,slave2:2181/solr instancedir \
--create testCollection ~/solr/myConfig
4、创建Collection上传到zookeeper
上传到zookeeper之后,其他节点就可以从zookeeper下载配置文件。接下来创建collection,命令:可指定分片数和副本数量
solrctl collection --create testIndex -s 3 -r 3 -m 50
创建上述步骤初始化的Collection,对Collection的属性进行配置。
solrctl --zk master:2181,slave1:2181,slave2:2181/solr collection \
--create testCollection -s 4 -r 2 -m 10
-s表示设置分片Shard数为4,建议设置为SolrCloud集群的机器数
-r表示设置的Replica数为1,创建的副本数为1,建议设置为2及其以上
-m默认值是1,表示最大shards数目,应大于Shard*Replica
-c是指定zk上solr/configs节点下使用的配置文件名称
-a是允许添加副本(必须写,否则创建不了副本)
注:使用如下指令查看是否创建Collection成功。
solrctl --zk master:2181,slave1:2181,slave2:2181/solr collection --list
三、HbaseIndexer部分
1.创建Lily HBase Indexer配置
在Hbase-solr的安装目录(cdh目录下面)下,创建Lily HBase Indexer配置文件morphline-hbase-mapper.xml 内容:
<?xml version="1.0" encoding="UTF-8"?>
<!-- table:需要索引的HBase表名称-->
<!-- mapper:用来实现和读取指定的Morphline配置文件类,固定为MorphlineResultToSolrMapper-->
<indexer table="solr_test_table" mapper="com.ngdata.hbaseindexer.morphline.MorphlineResultToSolrMapper">
<param name="morphlineFile" value="morphlines.conf"></param>
<param name="morphlineId" value="testIndexMap"></param>
</indexer>
其中morphlineId 的value是对应Key-Value Store Indexer 中配置文件Morphlines.conf 中morphlines 属性id值
Morphlines.conf 文件配置,下文有说明.
mkdir /testhbase/cdhsolr/testIndex/conf/hbase-indexer
cp /testhbase/cdhsolr/hbase-indexer/morphline-hbase-mapper.xml /testhbase/cdhsolr/testIndex/conf/hbase-indexer/morphline-hbase-mapper.xml
创建morphline-hbase-mapper-testCollection.xml文件,在该配置文件中加入以下内容:
<?xml version="1.0" encoding="UTF-8"?>
<indexer table="tableName" mapper="com.ngdata.hbaseindexer.morphline.MorphlineResultToSolrMapper" mapping-type="row">
<param name="morphlineFile" value="morphlines.conf"/>
<param name="morphlineId" value="morphlineOfTest"/>
2. 创建 Morphline 配置文件
具体操作:进入Key-Value Store Indexer面板->配置->查看和编辑->属性-Morphline文件,
首先需要在CDH中的Hbase Indexer中搜索并修改Morphline文件,修改内容如下:
SOLR_LOCATOR : {
zkHost : "$ZK_HOST"
}
morphlines : [
{
id : testIndexMap
importCommands : ["org.kitesdk.morphline.**", "com.ngdata.**"]
commands : [
{
extractHBaseCells {
mappings : [
{
inputColumn : "familyName:objType"
outputField : "objType"
type : string
source : value
}
{
inputColumn : "familyName:objId"
outputField : "objId"
type : string
source : value
}
{
inputColumn : "info:COL1"
outputField : "COL1"
type : string
source : value
},
{
inputColumn : "info:COL2"
outputField : "COL2"
type : string
source : value
}
]
}
}
{ logDebug { format : "output record: {}", args : ["@{}"] } }
]
}
]
id 为该Indexer的名字,
mapping中的内容为需要触发索引的字段,morphline-hbase-mapper-*.xml中的value
inputColumn 为待索引字段在hbase中相对应的 列族名:列名,
outputField 为待索引字段在Solr中的schema.xml中配置的相对应的field名。
若需要针对多个列族创建索引,则可以增加morphlines,每个morphlines之间以逗号进行分隔。
注:在CDH中修改内容后,还需将该文件拷贝一份并命名为morphlines.conf,放至~/solr/myConfig目录下(可更换为其它目录,后续指令的目录路径参数需要更改)。
3、注册Lily HBase Indexer configuration 和 Lily Hbase Indexer Service
hbase-indexer add-indexer \
--name testIndexer \
--indexer-conf ~/solr/myConfig/morphline-hbase-mapper-testCollection.xml \
--connection-param solr.zk=master:2181,slave1:2181,slave2:2181/solr \
--connection-param solr.collection=testCollection \
--zookeeper master:2181,slave1:2181,slave2:2181
可使用以下指令查看是否添加成功:hbase-indexer list-indexers
hbase-indexer list-indexers \
--zookeeper master:2181,slave1:2181,slave2:2181
4、同步数据,
put 'HBase_Indexer_Test','003','cf1:name','xiaofang'
put 'HBase_Indexer_Test','004','cf1:name','xiaogang'
5、批量同步索引,Hbase表数据到SOLR集群迁移
如果Hbase中已经存在数据,可使用以下hadoop程序将Hbase中现有的数据批量同步到Solr:
hadoop --config /etc/hadoop/conf.cloudera.yarn \
jar /opt/cloudera/parcels/CDH/lib/hbase-solr/tools/hbase-indexer-mr-*-job.jar \
-D 'mapred.child.java.opts=-Xmx1024m' \
--libjars /home/hadoop/package/lucene-analyzers-smartcn-4.10.3-cdh5.15.0.jar \
--conf /etc/hbase/conf/hbase-site.xml \
--log4j /opt/cloudera/parcels/CDH/share/doc/search*/examples/solr-nrt/log4j.properties \
--hbase-indexer-file ~/solr/myConfig/morphline-hbase-mapper-testCollection.xml \
--morphline-file ~/solr/myConfig/morphlines.conf \
--verbose \
--go-live \
--zk-host master:2181,slave1:2181,slave2:2181/solr \
--collection testCollection
在YARN的8088上也能看到MapReduce任务
5.扩展命令
Schema.xml新增索引字段,执行以下命令更新配置:
solrctl instancedir --update testIndex /testhbase/cdhsolr/testIndex
solrctl collection --reload testIndex
hbase-indexer update-indexer -n testIndex
添加多个索引器,设置多个indexer
一个solr 索引对应一个collection,一个collection对一个morphline-hbase-mapper.xml和Morphlines.conf。当需要配置多个索引时,在Morphlines.conf的morphlines节点添加对应索引模块,以id区分,如下面代码模块:
SOLR_LOCATOR :{
# ZooKeeper ensemble
zkHost :"$ZK_HOST"
}
morphlines: [ {
id: testIndexMap
importCommands: [“org.kitesdk.“, “com.ngdata.“]
commands: [ {
extractHBaseCells {
mappings: [ {
inputColumn: “info: COL1”
outputField: “COL1”
type: string
source: value
},
{
inputColumn: “info: COL2”
outputField: “COL2”
type: string
source: value
}
]
}
} {
logDebug {
format: “output record: {}”,
args: [“@ {}”]
}
} ]
},
{
id: testIndexMap2
importCommands: [“org.kitesdk.“, “com.ngdata.“]
commands: [ {
extractHBaseCells {
mappings: [ {
inputColumn: “info2: COL1”
outputField: “COL1”
type: string
source: value
},
{
inputColumn: “info2: COL2”
outputField: “COL2”
type: string
source: value
}
]
}
} {
logDebug {
format: “output record: {}”,
args: [“@ {}”]
}
} ]
}
]
附录指令
1.删除Hbase Indexer实例
hbase-indexer delete-indexer --name testIndexer \
--zookeeper master:2181,slave1:2181,slave2:2181
上述指令会将Hbase Indexer中的indexer实例进行删除,删除后对Indexers进行list将查询不到。
2.删除collection所有doc
solrctl collection --deletedocs testCollection
上述指令会将Solr中该Collection中的数据进行清空。
3.删除collection
solrctl collection --delete testCollection
上述指令会将SolrCloud中的Collection进行删除,在Solr的可视化界面将无法看到该Collection的分片数据。
4.删除instancedir
solrctl collection --list
solrctl instancedir --delete testCollection
上述指令会将Collection进行删除,并将zookeeper中包含该Collection信息的collection和config进行清空。
注意
如果需要彻底删除某个Collection,需按顺序执行以上四条指令,并将~/solr下的配置目录myConfig进行删除;
可以根据需求对以上指令进行选择性执行,但执行顺序不变。
二、读取HBase源表的数据,在Solr中建立索引
一种方案是通过HBase的普通API获取数据建立索引,此方案的缺点是效率较低每秒只能处理100多条数据(或许可以通过多线程提高效率):
public class SolrIndexer {
public static void main(String[] args) throws IOException,
SolrServerException {
final Configuration conf;
HttpSolrServer solrServer = new HttpSolrServer( "http://192.168.1.10:8983/solr");
// 因为服务端是用的Solr自带的jetty容器,默认端口号是8983
conf = HBaseConfiguration.create();
HTable table = new HTable(conf, "hb_tablename"); // 这里指定HBase表名称
Scan scan = new Scan();
scan.addFamily(Bytes.toBytes("d")); // 这里指定HBase表的列族
scan.setCaching(500);
scan.setCacheBlocks(false);
ResultScanner ss = table.getScanner(scan);
System.out.println("start ...");
int i = 0;
try {
for (Result r : ss) {
SolrInputDocument solrDoc = new SolrInputDocument();
solrDoc.addField("rowkey", new String(r.getRow()));
for (KeyValue kv : r.raw()) {
String fieldName = new String(kv.getQualifier());
String fieldValue = new String(kv.getValue());
if (fieldName.equalsIgnoreCase("time")
|| fieldName.equalsIgnoreCase("tebid")
|| fieldName.equalsIgnoreCase("tetid")
|| fieldName.equalsIgnoreCase("puid")
|| fieldName.equalsIgnoreCase("mgcvid")
|| fieldName.equalsIgnoreCase("mtcvid")
|| fieldName.equalsIgnoreCase("smaid")
|| fieldName.equalsIgnoreCase("mtlkid")) {
solrDoc.addField(fieldName, fieldValue);
}
}
solrServer.add(solrDoc);
solrServer.commit(true, true, true);
i = i + 1;
System.out.println("已经成功处理 " + i + " 条数据");
}
ss.close();
table.close();
System.out.println("done !");
} catch (IOException e) {
} finally {
ss.close();
table.close();
System.out.println("erro !");
}
}
}
另外一种方案是用到HBase的Mapreduce框架,分布式并行执行效率特别高,处理1000万条数据仅需5分钟,但是这种高并发需要对Solr服务器进行配置调优,不然会抛出服务器无法响应的异常:
MapReduce入口程序:
public class SolrHBaseIndexer {
private static void usage() {
System.err.println("输入参数: <配置文件路径> <起始行> <结束行>");
System.exit(1);
}
private static Configuration conf;
public static void main(String[] args) throws IOException,InterruptedException, ClassNotFoundException, URISyntaxException {
if (args.length == 0 || args.length > 3) {
usage();
}
createHBaseConfiguration(args[0]);
ConfigProperties tutorialProperties = new ConfigProperties(args[0]);
String tbName = tutorialProperties.getHBTbName();
String tbFamily = tutorialProperties.getHBFamily();
Job job = new Job(conf, "SolrHBaseIndexer");
job.setJarByClass(SolrHBaseIndexer.class);
Scan scan = new Scan();
if (args.length == 3) {
scan.setStartRow(Bytes.toBytes(args[1]));
scan.setStopRow(Bytes.toBytes(args[2]));
}
scan.addFamily(Bytes.toBytes(tbFamily));
scan.setCaching(500); // 设置缓存数据量来提高效率
scan.setCacheBlocks(false);
// 创建Map任务
TableMapReduceUtil.initTableMapperJob(tbName, scan,SolrHBaseIndexerMapper.class, null, null, job);
// 不需要输出
job.setOutputFormatClass(NullOutputFormat.class);
// job.setNumReduceTasks(0);
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
/**
* 从配置文件读取并设置HBase配置信息
*/
private static void createHBaseConfiguration(String propsLocation) {
ConfigProperties tutorialProperties = new ConfigProperties( propsLocation);
conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum", tutorialProperties.getZKQuorum());
conf.set("hbase.zookeeper.property.clientPort",tutorialProperties.getZKPort());
conf.set("hbase.master", tutorialProperties.getHBMaster());
conf.set("hbase.rootdir", tutorialProperties.getHBrootDir());
conf.set("solr.server", tutorialProperties.getSolrServer());
}
}
对应的Mapper:
public class SolrHBaseIndexerMapper extends TableMapper<Text, Text> {
public void map(ImmutableBytesWritable key, Result hbaseResult,
Context context) throws InterruptedException, IOException {
Configuration conf = context.getConfiguration();
HttpSolrServer solrServer = new HttpSolrServer(conf.get("solr.server"));
solrServer.setDefaultMaxConnectionsPerHost(100);
solrServer.setMaxTotalConnections(1000);
solrServer.setSoTimeout(20000);
solrServer.setConnectionTimeout(20000);
SolrInputDocument solrDoc = new SolrInputDocument();
try {
solrDoc.addField("rowkey", new String(hbaseResult.getRow()));
for (KeyValue rowQualifierAndValue : hbaseResult.list()) {
String fieldName = new String(
rowQualifierAndValue.getQualifier());
String fieldValue = new String(rowQualifierAndValue.getValue());
if (fieldName.equalsIgnoreCase("time")
|| fieldName.equalsIgnoreCase("tebid")
|| fieldName.equalsIgnoreCase("tetid")
|| fieldName.equalsIgnoreCase("puid")
|| fieldName.equalsIgnoreCase("mgcvid")
|| fieldName.equalsIgnoreCase("mtcvid")
|| fieldName.equalsIgnoreCase("smaid")
|| fieldName.equalsIgnoreCase("mtlkid")) {
solrDoc.addField(fieldName, fieldValue);
}
}
solrServer.add(solrDoc);
solrServer.commit(true, true, true);
} catch (SolrServerException e) {
System.err.println("更新Solr索引异常:" + new String(hbaseResult.getRow()));
}
}
}
参数配置文件“config.properties”:
HBASE_ZOOKEEPER_QUORUM=slave-1,slave-2,slave-3,slave-4,slave-5
HBASE_ZOOKEEPER_PROPERTY_CLIENT_PORT=2181
HBASE_MASTER=master-1:60000
HBASE_ROOTDIR=hdfs:///hbase
DFS_NAME_DIR=/opt/data/dfs/name
DFS_DATA_DIR=/opt/data/d0/dfs2/data
FS_DEFAULT_NAME=hdfs://192.168.1.10:9000
SOLR_SERVER=http://192.168.1.10:8983/solr
HBASE_TABLE_NAME=hb_app_m_user_te
HBASE_TABLE_FAMILY=d
三、结合Solr进行HBase数据的多条件查询:
可以通过web页面操作Solr索引,查询:
删除所有索引:
http://192.168.1.10:8983/solr/update/?stream.body=<delete><query>*:*</query></delete>&stream.contentType=text/xml;charset=utf-8&commit=true
通过java客户端结合Solr查询HBase数据:
public class QueryData {
public static void main(String[] args) throws SolrServerException, IOException {
final Configuration conf;
conf = HBaseConfiguration.create();
HTable table = new HTable(conf, "hb_app_m_user_te");
Get get = null;
List<Get> list = new ArrayList<Get>();
String url = "http://192.168.1.10:8983/solr";
SolrServer server = new HttpSolrServer(url);
SolrQuery query = new SolrQuery("time:201307 AND tetid:1 AND mgcvid:101 AND smaid:101 AND puid:102");
query.setStart(0); //数据起始行,分页用
query.setRows(10); //返回记录数,分页用
QueryResponse response = server.query(query);
SolrDocumentList docs = response.getResults();
System.out.println("文档个数:" + docs.getNumFound()); //数据总条数也可轻易获取
System.out.println("查询时间:" + response.getQTime());
for (SolrDocument doc : docs) {
get = new Get(Bytes.toBytes((String) doc.getFieldValue("rowkey")));
list.add(get);
}
Result[] res = table.get(list);
byte[] bt1 = null;
byte[] bt2 = null;
byte[] bt3 = null;
byte[] bt4 = null;
String str1 = null;
String str2 = null;
String str3 = null;
String str4 = null;
for (Result rs : res) {
bt1 = rs.getValue("d".getBytes(), "3mpon".getBytes());
bt2 = rs.getValue("d".getBytes(), "3mponid".getBytes());
bt3 = rs.getValue("d".getBytes(), "amarpu".getBytes());
bt4 = rs.getValue("d".getBytes(), "amarpuid".getBytes());
if (bt1 != null && bt1.length>0) {str1 = new String(bt1);} else {str1 = "无数据";} //对空值进行new String的话会抛出异常
if (bt2 != null && bt2.length>0) {str2 = new String(bt2);} else {str2 = "无数据";}
if (bt3 != null && bt3.length>0) {str3 = new String(bt3);} else {str3 = "无数据";}
if (bt4 != null && bt4.length>0) {str4 = new String(bt4);} else {str4 = "无数据";}
System.out.print(new String(rs.getRow()) + " ");
System.out.print(str1 + "|");
System.out.print(str2 + "|");
System.out.print(str3 + "|");
System.out.println(str4 + "|");
}
table.close();
}
}
小结:
通过测试发现,结合Solr索引可以很好的实现HBase的多条件查询,同时还能解决其两个难点:分页查询、数据总量统计。
实际场景中大多都是分页查询,分页查询返回的数据量很少,采用此种方案完全可以达到前端页面毫秒级的实时响应;若有大批量的数据交互,比如涉及到数据导出,实际上效率也是很高,十万数据仅耗时10秒。
另外,如果真的将Solr纳入使用,Solr以及HBase端都可以不断进行优化,比如可以搭建Solr集群,甚至可以采用SolrCloud基于hadoop的分布式索引服务。
总之,HBase不能多条件过滤查询的先天性缺陷,在Solr的配合之下可以得到较好的弥补,难怪诸如新蛋科技、国美电商、苏宁电商等互联网公司以及众多游戏公司,都使用Solr来支持快速查询。
实测,查询索引数据,确实相当快,main方法测试,7200条数据,只需要1秒。查询hbase数据,同样数量数据,个列,用了5秒,之前我们在3台集群上测试hbase查询,1万条,用了3秒,这里应该还有提升空间.
一般情况下,如果设置分页,那么这种速度是够用的,不过我们的情况多是把查询出的数据作为下一步计算的中间数据,因此,对查询效率要求比较高.还有,数据的导出,也会需要尽量快.
二、根据索引查询hbase中数据.
上一篇搭建了hbase的二级索引功能,只要hbase中有写数据,其就会自动的增量同步索引.