solr如何实现精确查询在Solr中实现多core查询

4677人阅读
搜索引擎(23)
Solr(16)
大体步骤同单核配置一样,可以参考《Windows下安装配置Solr (tomcat7.0)》。
1. 为什么要采用多核
1.1. 多核的目的
Solr Multicore 的目的一个solr实例,可以有多个搜索应用。
既然可以把不同类型的数据放到同一index中,也可以使用分开的多indexes。
基于这一点,你只需知道如何使用多 indexes(实际上就是运行Solr的多实例)。
尽管如此,为每一个类型添加一个完整的Solr实例会显得太臃肿庞大。
Solr 引入了Solr core的概念,该方案使用一个Solr实例管理多个indexes,这样就有热点core(hot core)的重读(reloading)与交换(swap,通常是读index与写index交换),那么管理一个 core或index也容易些。
每个Solr core由它自己的配置文件和索引数据组成。在多core执行搜索和索引几乎和没有使用core一样。只是添加core的名字为各自不同的URL。
单core情况下的如下搜索:
在多core环境下,你可以通过如下方式访问一个名为mbartists的core:
并非在URL中引入core name的参数名值对,而是用不同的context。这样就可以像在单core中执行你的管理任务,搜索,更新操作。
1.2. 为何使用多核
Solr实例支持多core比启用多index要好(do more)。多core同时解决了在生产环境下的一些关键需求:&
  (1) 重建索引
  (2) 测试配置变更&
  (3) 合并索引
  (4) 运行时重命名core
为何多core不是默认的?
多core是1.3版本中才加的,1.4后更成熟。
我们强烈建议你使用多core,既是你现在的solr.xml只配置了一个core,虽然会比单个索引稍复杂,但可以带来管理core上的好处。或许一天单个core可能最终RELOAD and STATUS命令,又或许单个core最终会被废禁。
多个core会是Solr将来支持大规模分布式索引的关键。因此,以后可以期待更多。
你可以得到更多的关于Solr的资料:。
2. 怎么配置多核
2.1.. 在准备solr实例的运行目录时,有所变更
依然将 D:\apache\solrhome,作为solr实例的运行目录
把下载的solr包中的example/multicore文件夹下面的所有文件放入到D:\apache\solrhome里面。
2.2. 运行检测
3. 多核配置文件
3.1. 怎么添加新的核
3.1.1. 配置文件中先添加新核,solrhome目录下的solr.xml
& &cores adminPath=&/admin/cores&&
&&& &core name=&core0& instanceDir=&core0& /&
&&& &core name=&core1& instanceDir=&core1& /&
&&& &core name=&liuweitoo& instanceDir=&liuweitoo& /&
& &/cores&
&其中,name是核的名称,instanceDir是新实例对应的目录名,该实例的配置文件都要放在该目录下。
3.1.2. solrhome目录复制新的文件夹
3.2. 多核配置文件解析
3.2.1. solr.xml
(这只是默认文件,当然也可以指定别的文件),如:
&?xml version=&1.0& encoding=&UTF-8& ?&&
&&solr persistent=&false&&&&&
&& &cores adminPath=&/admin/cores&&&
&&&& &core name=&core0& instanceDir=&core0& /&&
&&&& &core name=&core1& instanceDir=&core1& /&&
&& &/cores&&
这个文件是告诉solr应该加载哪些core,&cores&……&/cores&里有 core0、core1。core0(可以类比以前的solr.home)/conf目录下有schema.xml与solrconfig.xml,可以把实际应用的/solr/conf/schema.xml复制过来(注意:solrconfig.xml不要复制)。
3.2.2. 启动tomcat,访问应用,就可以看到有 Admin core0 和 Admin core1
3.2.3. 一些关键的配置
Persistent=&false&指明运行时的任何修改我们不做保存。如拷贝。如果你想保存从启动起的一些改动,那就把 persistent设置为true。
如果你的index策略是完成建index到一个纯净的core中然后交换到活动core 那么你绝对应该设为true。
sharedLib=&lib&指明了所有core的jar文件的lib目录。
如果你有一个core有自己需要的jar文件,那么你可以把他们置入到core/lib目录。
例如:karaoke core 使用 Solr Cell来索引化富文本内容,因此那些用来解析和抽取富文本的jar文件被放到./examples/cores/karaoke/lib/.
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:196411次
积分:2163
积分:2163
排名:第15523名
原创:49篇
评论:17条
(1)(4)(39)(8)关于SolrCore引发的总结---分布式搜索实现
&-Solr3.* 分布式搜索实现分析
本部分承接&详细理解内容见中文注释。
1 总体描述:
在solr分布式搜索框架基础上(所有请求在管道流程中,进入handlerRequestBody逻辑,handleRequestBody
这里面,执行遍历每个SearchComponent的
prepare、process、after三阶段逻辑,并且searchcomponent是按配置顺序来遍历执行),实现上有关键概念:线程、分阶段、链接池三大核心技术来完成分布式搜索。
功能的扩展,SearchComponent 的
横向扩展(分布式)、纵向扩展(配置多个SearchComponent,会执行循环处理,每个阶段,只是符合特定条件的逻辑才触发)。详细流程可以参考上一部分的关于SolrCore引发的总结的updateRequest
部分的分解。
框架的扩展,在handler这一层,handler总领handleRequestBody,进而总领这个handler配置的SearchComponent,而具体的SearchComponent会有本地和分布式两块逻辑。如果是分布式的,在分布式HttpCommComponent
中来协调分阶段循环、线程、连接池的管理
2 详细分析部分
code version&solr3.4&
org.apache.ponent&
SearchHandler extends RequestHandlerBase implements
SolrCoreAware
voidhandleRequestBody(SolrQueryRequest
req, SolrQueryResponse rsp)
throws Exception,
ParseException, InstantiationException,
IllegalAccessException
// int sleep = req.getParams().getInt("sleep",0);
// if (sleep & 0) {log.error("SLEEPING for " +
sleep);& Thread.sleep(sleep);}
ResponseBuilder rb =
new ResponseBuilder();
rb.components=
components;
rb.setDebug(req.getParams().getBool(CommonParams.DEBUG_QUERY,
finalRTimer timer =
rb.isDebug() ?
new RTimer() :
if(timer ==
// non-debugging prepare phase
for( SearchComponent c
components) {
c.prepare(rb);
// debugging prepare phase
RTimer subt = timer.sub(
"prepare" );
for( SearchComponent c
components) {
rb.setTimer( subt.sub( c.getName() ) );
c.prepare(rb);
rb.getTimer().stop();
subt.stop();
if(rb.shards==
// a normal non-distributed request
// The semantics of debugging vs not debugging are different
enough that
// it makes sense to have two control loops
if(!rb.isDebug())
// Process
for( SearchComponent c
components ) {
c.process(rb);
// Process
RTimer subt = timer.sub(
"process");
for( SearchComponent c
components ) {
rb.setTimer( subt.sub( c.getName() ) );
c.process(rb);
rb.getTimer().stop();
subt.stop();
timer.stop();
// add the timing info
if( rb.getDebugInfo()
rb.setDebugInfo(
newSimpleOrderedMap()
rb.getDebugInfo().add(
"timing", timer.asNamedList()
// a distributed request
//分布式处理部分。comm里面引用client,compoment的prepare
两部分是相同的。在process过程出现分布or
非分布式分支
HttpCommComponent comm =
new HttpCommComponent();
//总管对象,接管请求之前、之后的逻辑,负责网络的任务提交、返回的封装。
if(rb.outgoing==
rb.outgoing
new LinkedList();
rb.finished=
newArrayList();
intnextStage = 0;
nextStage = ResponseBuilder.STAGE_DONE;
//call all components
尽管调用了所有component,但是并不是所有component都有分布式支持
//3.4只有QueryComponent、FacetComponent实现了distributedProcess
//其他的searchcomponent
直接返回state.done
//在searchcomponent的distributedProcess中生成outgoing
list 的元素。
//QueryComponet
第一轮循环,STAGE_EXECUTE_QUERY,生成query请求,第二轮循环STAGE_GET_FIELDS
生成query请求。FacetComponet
只有一个阶段
//这里循环才是change
state的入口,由最外层do
触发。也是理解分阶段并发的关键。
for( SearchComponent c
components ) {
// the next stage is the minimum of what all components
nextStage = Math.min(nextStage,
c.distributedProcess(rb));
// check the outgoing queue and send requests
while (rb.outgoing.size()
//向所有shard对象执行query请求,也就是第一轮时候,所有shard
都执行相同的
请求,处理query
execute,请求状态统一在发起结点控制,等所有shards第一阶段完成后,开始第二阶段的请求,getfields,并执行第二阶段的等待和finished。每一个阶段结束,会执行相应的comm.canceAll
comm没有close。实际上
SearchComponent的distributedProcess,也即
querycomponent
生成request,而facetcomponent的distributed
第一阶段提交之后,等待返回,执行handlerResponse任务,会根据sreps的purpose决定本阶段的结束的工作,其实,只有getTopIDs
才会执行megeIds,第一阶段createMainqueyr中会设置sreq
的purpose。
第一阶段提交并且全部返回后,这里是来一个response就执行一个。最后,执行finished操作,其中只有在getfiled
state也就是第二阶段的getfield的时候才有效。参见Querycomponent、FacetComponent的finished部分
第二阶段,对应do
此时 searchercomponent
的distrubutedProcess执行了状态的调整和提交本阶段的请求,此时outging的size
全部shard都接受到请求,对请求做本阶段提交的waiting、comcancleALL、handlerResponse,
接着退出等待,处理finished任务
// submit all current request tasks at once
while (rb.outgoing.size()
&&&&&&&&&&&
ShardRequest sreq = rb.outgoing.remove(0);
&&&&&&&&&&&
sreq.actualShards
sreq.shards;
&&&&&&&&&&&
if (sreq.actualShards==ShardRequest.ALL_SHARDS)
&&&&&&&&&&&&&
sreq.actualShards
rb.shards;
&&&&&&&&&&&
&&&&&&&&&&&
sreq.responses
new ArrayList();
//所有shard
都执行提交,但是状体的协调其实都在当前这个分发结点,也就执行merge的结点
&&&&&&&&&&&
map from shard to address[]
&&&&&&&&&&&
for (String shard :
sreq.actualShards)
&&&&&&&&&&&&&
ModifiableSolrParams params =
newModifiableSolrParams(sreq.params);
&&&&&&&&&&&&&
params.remove(ShardParams.SHARDS);&&&&&
// not a top-level request
&&&&&&&&&params.remove("indent");
&&&&&&&&&&&&&
params.remove(CommonParams.HEADER_ECHO_PARAMS);
&&&&&&&&&&&&&
params.set(ShardParams.IS_SHARD,
// a sub (shard) request
&&&&&&&&&&&&&
String shardHandler =
req.getParams().get(ShardParams.SHARDS_QT);
&&&&&&&&&&&&&
if (shardHandler ==
&&&&&&&&&&&&&&&
params.remove(CommonParams.QT);
&&&&&&&&&&&&&
&&&&&&&&&&&&&&&
params.set(CommonParams.QT,
shardHandler);
&&&&&&&&&&&&&
&&&&&&&&&&&&&
comm.submit(sreq, shard, params);
&&&&&&&&&&&
//提交第一阶段完毕后,outgoing.size=0,开始等待第一阶段的全部resp,并执行component的handlerResponse,进入第二阶段的query
execut的时候,才会有mergerid()处理或者
第三阶段的
阶段createRetrieveDocs
请求之后才有returnFields()
这里是while 循环体内&
ShardResponse srsp = comm.takeCompletedOrError();
一直在等待shard的当前阶段的返回。也就是说mergerId、getFields都是动态执行的。
// now wait for replies, but if anyone puts more requests
// the outgoing queue, send them out immediately (by
// this loop)
while (rb.outgoing.size()
&&&&&&&&&&&
ShardResponse srsp = comm.takeCompletedOrError();//等待过程
等待全部提交的返回,可能有慢待?
&&&&&&&&&&&
if (srsp ==
// no more requests to wait for
&&&&&&&&&&&
// Was there an exception?& If so, abort
everything and
&&&&&&&&&&&
// rethrow
&&&&&&&&&&&
if (srsp.getException()
&&&&&&&&&&&&&
comm.cancelAll();//每个阶段的所有pending cancel
&&&&&&&&&&&&&
if (srsp.getException()
instanceof SolrException)
&&&&&&&&&&&&&&&
throw (SolrException)srsp.getException();
&&&&&&&&&&&&&
&&&&&&&&&&&&&&&
new SolrException(SolrException.ErrorCode.SERVER_ERROR,
srsp.getException());
&&&&&&&&&&&&&
&&&&&&&&&&&
&&&&&&&&&&&
rb.finished.add(srsp.getShardRequest());
&&&&&&&&&&&
// let the components see the responses to the request
&&&&&&&&&&&
for(SearchComponent c
components) {
&&&&&&&&&&&&&
c.handleResponses(rb, srsp.getShardRequest());
//遍历了component了,但是不是合适的purpose,等于没有操作。而purpose在阶段任务提交时做了设置
&&&&&&&&&&&
//其中在getfield阶段时,才有效果。对之前阶段的预分配的doc执行过滤,egtop
merger默认
10*shard,由于可能重复,此时mergerid
结果小于10*shard
for(SearchComponent c
components) {
&&&&&&&&&&&
c.finishStage(rb);
// we are done when the next stage is MAX_VALUE
while(nextStage !=
Integer.MAX_VALUE);
/////////////////////////////////////////
Solr 搜索框架下的分布式搜索
generalize how a comm component can fit into search
component framework
statics should be per-core singletons
class HttpCommComponent
// We want an executor that doesn't take up any resources
// it's not used, so it could be created statically for
// the distributed search component if desired.
// Consider CallerRuns policy and a lower max threads to
// requests at some point (or should we simply return
staticExecutor
commExecutor=
newThreadPoolExecutor(
Integer.MAX_VALUE,
5, TimeUnit.SECONDS,
// terminate idle threads after 5 sec
new SynchronousQueue()&
// directly hand off tasks
staticHttpClient
client; //static 表明一个solr
instance 共享一个client,也往往一个jvm对应一个client
MultiThreadedHttpConnectionManager mgr =
new MultiThreadedHttpConnectionManager();
mgr.getParams().setDefaultMaxConnectionsPerHost(20);//链接数有点少啊
//可能导致的一个问题:请求超时,导致connect
mgr.getParams().setMaxTotalConnections(10000);
mgr.getParams().setConnectionTimeout(SearchHandler.connectionTimeout);
mgr.getParams().setSoTimeout(SearchHandler.soTimeout);
// mgr.getParams().setStaleCheckingEnabled(false);
newHttpClient(mgr);&&&
CompletionService
completionService =
newExecutorCompletionService(commExecutor);
newHashSet&();
HttpCommComponent() {
privatestaticclassSimpleSolrResponse
extends SolrResponse {
longelapsedTime;
NamedList&nl;
publiclonggetElapsedTime()
returnelapsedTime;
publicNamedListgetResponse()
publicvoidsetResponse(NamedListrsp)
voidsubmit(finalShardRequest
final String shard,
final ModifiableSolrParams
Callable task =
new Callable() {
publicShardResponse
throws Exception {
ShardResponse srsp =
new ShardResponse();
srsp.setShardRequest(sreq);
srsp.setShard(shard);
SimpleSolrResponse ssr =
new SimpleSolrResponse();
srsp.setSolrResponse(ssr);
long startTime =
System.currentTimeMillis();
// String url = "http://" + shard +
"/select";
String url =
"http://" +
params.remove(CommonParams.WT);
// use default (currently javabin)
params.remove(CommonParams.VERSION);
SolrServer server =
newCommonsHttpSolrServer(url,
// SolrRequest req = new
QueryRequest(SolrRequest.METHOD.POST, "/select");
// use generic request to avoid extra processing of
QueryRequest req =
new QueryRequest(params);
req.setMethod(SolrRequest.METHOD.POST);
// no need to set the response parser as binary is the
// req.setResponseParser(new BinaryResponseParser());
// srsp.rsp = server.request(req);
// srsp.rsp = server.query(sreq.params)&
server.request(req);
catch (Throwable th)
srsp.setException(th);
instanceof SolrException)
&&&&&&&&&&&
srsp.setResponseCode(((SolrException)th).code());
&&&&&&&&&&&
srsp.setResponseCode(-1);
ssr.elapsedTime
System.currentTimeMillis() - startT
pending.add(
completionService.submit(task)
ShardResponse take() {
while(pending.size()
Future future =
completionService.take();
pending.remove(future);
ShardResponse rsp = future.get();
rsp.getShardRequest().responses.add(rsp);
if (rsp.getShardRequest().responses.size()
== rsp.getShardRequest().actualShards.length)
catch(InterruptedException e)
new SolrException(SolrException.ErrorCode.SERVER_ERROR,
catch(ExecutionException e)
// should be impossible... the problem with catching the
// at this level is we don't know what ShardRequest it applied
new SolrException(SolrException.ErrorCode.SERVER_ERROR,
"Impossible Exception",e);
returnnull;
ShardResponse takeCompletedOrError() {
while(pending.size()
Future future =
completionService.take();
pending.remove(future);
ShardResponse rsp = future.get();
if (rsp.getException()
// if exception, return immediately
// add response to the response list... we do this after the take()
// not after the completion of "call" so we know when the last
// for a request was received.& Otherwise we might
return the same
// request more than once.
rsp.getShardRequest().responses.add(rsp);
if (rsp.getShardRequest().responses.size()
== rsp.getShardRequest().actualShards.length)
catch(InterruptedException e)
new SolrException(SolrException.ErrorCode.SERVER_ERROR,
catch(ExecutionException e)
// should be impossible... the problem with catching the
// at this level is we don't know what ShardRequest it applied
new SolrException(SolrException.ErrorCode.SERVER_ERROR,
"Impossible Exception",e);
returnnull;
voidcancelAll() {
for(Future future :
pending) {
any issues with interrupting?& shouldn't be
// there are finally blocks to release connections.
future.cancel(true);
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。技术分享:如何用Solr搭建大数据查询平台 - 推酷
技术分享:如何用Solr搭建大数据查询平台
*原创作者:
0&00 开头照例扯淡
自从各种脱裤门事件开始层出不穷,在下就学乖了,各个地方的密码全都改成不一样的,重要帐号的密码定期更换,生怕被人社出祖宗十八代的我,甚至开始用起了假名字,我给自己起一新网名”兴才”,这个看起来还不错的名字,其实是我们家乡骂人土话,意思是脑残人士…. -_-|||额好吧,反正是假的,不要在意这些细节。
这只是名,至于姓氏么,每个帐号的注册资料那里,照着百家姓上赵钱孙李周吴郑王的依次往下排,什么张兴才、李兴才、王兴才……于是也不知道我这样”兴才”了多久,终于有一天,我接到一个陌生电话:您好,请问是马兴才先生吗?
好么,该来的终于还是来了,于是按名索骥,得知某某网站我用了这个名字,然后通过各种途径找,果然,那破站被脱裤子了。果断Down了那个裤子,然后就一发不可收拾,走上了收藏裤子的不归路,直到有一天,我发现收藏已经非常丰富了,粗略估计得好几十亿条数据,拍脑袋一想,这不能光收藏啊,我也搭个社工库用吧……
社工库怎么搭呢,这种海量数据的东西,并不是简单的用mysql建个库,然后做个php查询select * from sgk where username like ‘%xxxxx%’这样就能完事的,也不是某些幼稚骚年想的随便找个4g内存,amd双核的破电脑就可以带起来的,上面这样的语句和系统配置,真要用于社工库查询,查一条记录恐怕得半小时。好在这个问题早就被一种叫做全文搜索引擎的东西解决了,更好的消息是,全文搜索引擎大部分都是开源的,不需要花钱。
目前网上已经搭建好的社工库,大部分是mysql+coreseek+php架构,coreseek基于sphinx,是一款优秀的全文搜索引擎,但缺点是比较轻量级,一旦数据量过数亿,就会有些力不从心,并且搭建集群做分布式性能并不理想,如果要考虑以后数据量越来越大的情况,还是得用其他方案,为此我使用了solr。
Solr的基础是著名的Lucene框架,基于java,通过jdbc接口可以导入各种数据库和各种格式的数据,非常适合开发企业级的海量数据搜索平台,并且提供完善的solr cloud集群功能,更重要的是,solr的数据查询完全基于http,可以通过简单的post参数,返回json,xml,php,python,ruby,csv等多种格式。
以前的solr,本质上是一组servlet,必须放进Tomcat才能运行,从solr5开始,它已经自带了jetty,配置的好,完全可以独立使用,并且应付大量并发请求,具体的架构我们后面会讲到,现在先来进行solr的安装配置。
0&02 安装和配置
以下是我整个搭建和测试过程所用的硬件和软件平台,本文所有内容均在此平台上完成:
软件配置: solr5.5,mysql5.7,jdk8,Tomcat8& Windows10/Ubuntu14.04 LTS
硬件配置: i7 4770k,16G DDR3,2T西数黑盘
2.1 &mysql数据库
Mysql数据库的安装和配置我这里不再赘述,只提一点,对于社工库这种查询任务远远多于插入和更新的应用来说,最好还是使用MyISAM引擎。搭建好数据库后,新建一个库,名为newsgk,然后创建一个表命名为b41sgk,结构如下:
id& bigint 主键 自动增长
varchar 用户名
varchar 邮箱
password varchar 密码
salt varchar 密码中的盐或者第二密码
ip varchar ip、住址、电话等其他资料
site varchar 数据库的来源站点
接下来就是把收集的各种裤子全部导入这个表了,这里推荐使用navicat,它可以支持各种格式的导入,具体过程相当的枯燥乏味,需要很多的耐心,这里就不再废话了,列位看官自己去搞就是了,目前我初步导入的数据量大约是10亿条。
2.2 Solr的搭建和配置
首先下载solr:
$ tar zxvf solr-5.5.0.tgz
安装jdk8:
$ sudo add-apt-repository ppa:webupd8team/java$ sudo apt-get update$ sudo apt-get install oracle-java8-installer$ sudo apt-get install oracle-java8-set-default
因为是java跨平台的,Windows下和linux下solr是同一个压缩包,windows下jdk的安装这里不再说明。
进入解压缩后的solr文件夹的bin目录,solr.cmd和solr分别是windows和linux下的启动脚本:
因为社工库是海量大数据,而jvm默认只使用512m的内存,这远远不够,所以我们需要修改,打开solr.in.sh文件,找到这一行:
SOLR_HEAP=”512m”
依据你的数据量,把它修改成更高,我这里改成4G,改完保存. 在windows下略有不同,需要修改solr.in.cmd文件中的这一行:
set SOLR_JAVA_MEM=-Xms512m -Xmx512m
同样把两个512m都修改成4G。
Solr的启动,重启和停止命令分别是:
$ ./solr start$ ./solr restart –p 8983$ ./solr stop –all
在linux下还可以通过install_solr_service.sh脚本把solr安装为服务,开机后台自动运行。
Solr安装完成,现在我们需要从mysql导入数据,导入前,我们需要先创建一个core,core是solr的特有概念,每个core是一个查询、数据,、索引等的集合体,你可以把它想象成一个独立数据库,我们创建一个新core:
在solr-5.5.0/server/solr子目录下面建立一个新文件夹,命名为solr_mysql,这个是core的名称,在下面创建两个子目录conf和data,把solr-5.5.0/solr-5.5.0/example/example-DIH/solr/db/conf下面的所有文件全部拷贝到我们创建的conf目录中.接下来的配置主要涉及到三个文件, solrconfig.xml, schema.xml和db-data-config.xml。
首先打开db-data-config.xml,修改为以下内容:
&dataConfig&
&dataSource name=&sgk& type=&JdbcDataSource& driver=&com.mysql.jdbc.Driver& url=&jdbc:mysql://127.0.0.1:3306/newsgk& user=&root& password=&password& batchSize=&-1& /&
&document name=&mysgk&&
&entity name=&b41sgk& pk=&id& query=&select * from b41sgk&&
&field column=&id& name=&id&/&
&field column=&username& name=&username&/&
&field column=&email& name=&email&/&
&field column=&password& name=&password&/&
&field column=&salt& name=&salt&/&
&field column=&ip& name=&ip&/&
&field column=&site& name=&site&/&
&/document&&/dataConfig&
这个文件是负责配置导入数据源的,请按照mysql实际的设置修改datasource的内容,下面entity的内容必须严格按照mysql中社工库表的结构填写,列名要和数据库中的完全一样。
然后打开solrconfig.xml,先找到这一段:
&schemaFactory class=&ManagedIndexSchemaFactory&&
&bool name=&mutable&&true&/bool&
&str name=&managedSchemaResourceName&&managed-schema&/str&
&/schemaFactory&
把它全部注释掉,加上一行,改成这样:
&!-- &schemaFactory class=&ManagedIndexSchemaFactory&&
&bool name=&mutable&&true&/bool&
&str name=&managedSchemaResourceName&&managed-schema&/str&
&/schemaFactory&--&
&schemaFactory class=&ClassicIndexSchemaFactory&/&
这是因为solr5 以上默认使用managed-schema管理schema,需要更改为可以手动修改。
然后我们还需要关闭suggest,它提供搜索智能提示,在社工库中我们用不到这样的功能,重要的是,suggest会严重的拖慢solr的启动速度,在十几亿数据的情况下,开启suggest可能会导致solr启动加载core长达几个小时!
同样在solrconfig.xml中,找到这一段:
mySuggester
FuzzyLookupFactory
DocumentDictionaryFactory
把这些全部删除,然后保存solrconfig.xml文件。
接下来把managed-schema拷贝一份,重命名为schema.xml (原文件不要删除),打开并找到以下位置:
只保留_version_和_root_节点,然后把所有的field,dynamicField和copyField全部删除,添加以下的部分:
&field name=&id& type=&int& indexed=&true& stored=&true& required=&true& multiValued=&false& /&
&field name=&username& type=&text_ik& indexed=&true& stored=&true&/&
&field name=&email& type=&text_ik& indexed=&true& stored=&true&/&
&field name=&password& type=&text_general& indexed=&true& stored=&true&/&
&field name=&salt& type=&text_general& indexed=&true& stored=&true&/&
&field name=&ip& type=&text_general& indexed=&true& stored=&true&/&
&field name=&site& type=&text_general& indexed=&true& stored=&true&/&
&field name=&keyword& type=&text_ik& indexed=&true& stored=&false& multiValued=&true&/&
&copyField source=&username& dest=&keyword&/&
&copyField source=&email& dest=&keyword&/&
&uniqueKey&id&/uniqueKey&
这里的uniqueKey是配置文件中原有的,用来指定索引字段,必须保留。新建了一个字段名为keyword,它的用途是联合查询,即当需要同时以多个字段做关键字查询时,可以用这一个字段名代替,增加查询效率,下面的copyField即用来指定复制哪些字段到keyword。注意keyword这样的字段,后面的multiValued属性必须为true。
username和email以及keyword这三个字段,用来检索查询关键字,它们的类型我们指定为text_ik,这是一个我们创造的类型,因为solr虽然内置中文分词,但效果并不好,我们需要添加IKAnalyzer中文分词引擎来查询中文。在
下载IKAnalyzer for solr5的源码包,然后使用Maven编译,得到一个文件IKAnalyzer-5.0.jar,把它放入solr-5.5.0/server/solr-webapp/webapp/WEB-INF/lib目录中,然后在solrconfig.xml的fieldType部分加入以下内容:
&fieldType name=&text_ik& class=&solr.TextField&&
&analyzer type=&index& useSmart=&false& class=&org.wltea.analyzer.lucene.IKAnalyzer&/&
&analyzer type=&query& useSmart=&true& class=&org.wltea.analyzer.lucene.IKAnalyzer&/&
&/fieldType&
保存后,core的配置就算完成了,不过要导入mysql数据,我们还需要在mysql网站上下载mysql-connector-java-bin.jar库文件,连同solr-5.5.0/dist目录下面的solr-dataimporthandler-5.5.0.jar,solr-dataimporthandler-extras-5.5.0.jar两个文件,全部拷贝到solr-5.5.0/server/solr-webapp/webapp/WEB-INF/lib目录中,然后重启solr,就可以开始数据导入工作了。
2.3 数据导入
确保以上配置完全正确且solr已经运行,打开浏览器,访问
,进入solr的管理页面,点击左侧Core Admin,然后Add Core:
name和instanceDir都填写前面我们命名的core名称solr_mysql,点击add core后,稍等片刻,core就建立完成了。此时在左边的下拉菜单选择创建的core,然后进一步选择Dataimport项,按照如下设置:
点击Execute,就会开始从mysql导入数据,选中Auto-Refresh Status会自动刷新进度,接下来就是漫长的等待……
导入完成后,我们就可以开始查询了,solr的查询全部使用post参数,比如:
因为前面已经建立了复合字段keyword,所以这里我们直接用keyword:会自动查找username和email中包含的所有结果,start=10&rows=100指定查询结果返回第11行到第110行的内容,因为solr采用的是分页查询,wt=json指定查询结果是json格式的,还可以是xml、php、python、ruby以及csv。
上图返回结果中的numfound:111892代表一共返回的结果数,不指定 start和rows的情况下默认只显示前十个结果。还需要注意IKAnalyzer引擎的几个问题,在以纯数字或者纯字母关键字查询时,IKAnalyzer会返回正确的结果,但在查询数字字母混合关键字时,需要在后面加*号,查询汉字时.默认会进行分词,即把一段关键字分成几个词查询,而社工库必须精确查询,所以汉字查询必须给关键字加双引号。
到这一步,如果只是搭建一个本地库,供自己使用,那么我们接下来只需写一个查询程序,post关键字,然后显示返回的结果即可,比如这样:
秒查,速度非常快,但如果要架设成服务器,提供给其他人使用,我们还有很多工作要做。
0&03 服务器架构和数据增量更新
尽管solr现在已经自带了jetty,jetty并不弱于tomcat,且没有后者那么臃肿,但是很多人在构建web应用时还是喜欢用以前的习惯,把solr整合进tomcat,然后和后台程序一锅乱炖,坦白说,在下并不喜欢这样的架构,对于大数据应用来说,各个功能组件各自独立,互相配合远比大杂烩要有效率和易于维护的多,所以,我理想中的社工库查询服务器,应该是以下的架构:
以上架构中,mysql只负责存储整理好的数据,并不提供查询服务,整理和导入新数据库时,只需操作mysql,solr利用自带的jetty独立运行,定期从mysql导入增量更新的数据,Tomcat作为应用服务器,运行提供查询的servlet应用,此应用通过http向solr post数据并获取结果,返回给前端页面,相互独立又相辅相成。
并且,solr并不依赖于mysql,它本身就是数据库可以独立运行,而社工库这种东西,并不是经常有新数据的,获取新数据的间隔可能很长,所以上面的定时增量更新可以改为手动增量更新,没有新数据时mysql完全可以关闭以节约资源。
那么我们先开始着手增量更新的设置,我们现在已有的数据表b41sgk并不动,在此基础上建立一个和b41sgk结构基本相同的表b41new,不同之处是增加了一个字段updatetime,用来自动存储添加数据的时间,以后的新数据全写入这个表,b41sgk不再做更新:
CREATE TABLE b41new (
id bigint NOT NULL auto_increment,
username varchar(255),
email varchar(255),
password varchar(255),
salt varchar(255),
ip varchar(255),
site varchar(255),
updatetime timestamp NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (‘id’)
用和前面同样的步骤建立一个core命名为solr_newsgk,在db-data-config.xml中做如下设置:
&dataConfig&
&dataSource name=&sgk& type=&JdbcDataSource& driver=&com.mysql.jdbc.Driver& url=&jdbc:mysql://127.0.0.1:3306/newsgk& user=&root& password=&password& batchSize=&-1& /&
&document name=&mysgk&&
&entity name=&b41new& pk=&id& query=&select * from b41new&
deltaQuery=&select id from b41new where updatetime & '${dataimporter.last_index_time}'&
deltaImportQuery=&select * from b41new where id='${dataimporter.delta.id}'&&
&field column=&id& name=&id&/&
&field column=&username& name=&username&/&
&field column=&email& name=&email&/&
&field column=&password& name=&password&/&
&field column=&salt& name=&salt&/&
&field column=&ip& name=&ip&/&
&field column=&site& name=&site&/&
&/document&&/dataConfig&
last_index_time和delta.id是两个自动变化的参数,分别记录最后一次导入数据的时间和已导入的最大id值,存储于当前core的conf目录下dataimporter.properties文件中,以上设置保存后,提交如下链接:
如果此时数据表b41new中已经添加了新数据,就会自动增量同步到solr中,如果要每天定时自动增量更新,执行:
$ crontab –e
增加一条:
0 0 * * * curl –s &
保存后执行:
$ sudo service cron restart
Solr就会在每天的零时自动增量导入数据,如果是windows系统,可以利用powershell和计划任务达到同样的目的。
现在我们的服务器搭建还剩下最后一件事:既然现在我们准备把solr查询提供给别人用,那么问题来了,我们只希望别人通过tomcat里的servlet查询,而不希望直接调用solr,我们需要屏蔽外部查询:
$ sudo iptables -I INPUT -p tcp --dport 8983 -j DROP$ sudo iptables -I INPUT -s 127.0.0.1 -p tcp --dport 8983 -j ACCEPT
如果把solr和tomcat放在不同的服务器,只需把127.0.0.1改成tomcat服务器的ip地址。接下来保存:
$ sudo su# iptables-save & /etc/iptables.up.rules
编辑/etc/network/interfaces,加入下面这行:
pre-up iptables-restore & /etc/iptables.up.rules
0&04 编写查询应用
Solr除了可以通过http post数据来查询之外,还提供了一套完整的api solrj,其实solrj底层还是通过http访问的,但如果你是用java开发,使用它会比直接http访问方便的多。
我们启动eclipse,配置好和tomcat的连接,新建一个项目sgk,在构建路径中添加solr-5.5.0/dist/solrj-lib下的全部jar包,然后添加solr-5.5.0/server/lib/ext中的log4j-1.2.17.jar,slf4j-api-1.7.7.jar,slf4j-log4j12-1.7.7.jar三个包。
在web.xml中添加如下设置:
&context-param&
&param-name&solraddr&/param-name&
&param-value&http://localhost:8983/solr/solr_mysql&/param-value&
&/context-param&
&context-param&
&param-name&shards&/param-name&&param-value&localhost:8983/solr/solr_newsgk,localhost:8983/solr/solr_mysql&/param-value&
&/context-param&
&servlet-name&sgk&/servlet-name&
&servlet-class&com.baiker.sgk&/servlet-class&
&init-param&
&param-name&filter_keywords&/param-name&
&param-value&”|*&/param-value&
&/init-param&
&/servlet&
&servlet-mapping&
&servlet-name&sgk&/servlet-name&
&url-pattern&/sgk&/url-pattern&
&/servlet-mapping&
新建一个servlet命名为searcher:
package com.baiker.import java.io.IOE
import java.io.PrintW
import javax.servlet.ServletE
import javax.servlet.http.HttpS
import javax.servlet.http.HttpServletR
import javax.servlet.http.HttpServletR
import org.apache.solr.client.solrj.*;
import org.apache.solr.client.solrj.impl.HttpSolrC
import org.apache.solr.client.solrj.response.QueryR
import org.mon.SolrD
import org.mon.SolrDocumentL
import org.json.JSONA
import org.json.JSONOpublic class searcher extends HttpServlet {
private static final long serialVersionUID = 1L;
private static String baseURL = &&;
private static String shards = &&;
private static SolrC
private String[] colist ={&username&, &email&, &password&, &salt&, &ip&, &site&}; //定义要检索的字段列表
* 构造函数
public searcher() {
* 析构函数
public void destroy() {
super.destroy();
* 初始化servlet
public void init() throws ServletException {
filterk = this.getServletConfig().getInitParameter(&filter_keywords&); //从servlet配置中读取关键字过滤表
baseURL = this.getServletContext().getInitParameter(&solraddr&); //从全局配置中读取solr服务器地址
shards = this.getServletContext().getInitParameter(&shards&); //读取多core联合查询地址
solr = new HttpSolrClient(baseURL); //实例化SolrClient对象
* 无意义关键字屏蔽:检测搜索关键字是否可用
* @param str 关键字
* @return boolean
private boolean NotLegalString(String str){
boolean notlegal =
//以下预定义一些需要屏蔽的无意义搜索关键词,这些会耗费很多服务器资源,且返回的结果没有价值
String[] foollist = {&12345&,&123456&,&1234567&,&&,&&,&&,
&&,&&,&7654321&,&654321&,&54321&,&123123&,&112233&,&&,&&,&&,
&abcde&,&abcdef&,&abcdefg&,&abcdefgh&,&abcdefghi&,&abcdefghij&,&aaa123&,&abc123&,&bbb123&,&aabbcc&,&abcabc&,&abcdabcd&,&aaabbbccc&,&aabbccdd&,&ccc123&,&qwert&,&qwerty&,&asdfg&,&asdfgh&,&qazwsx&,&user&,&guest&,&admin&,&administrator&,&manager&};
for(String s : foollist){
if(str.equals(s))
char first = str.charAt(0); //检测关键字是否为一组重复字符
for (int i=1; i
return ErrorResult(&关键词长度不能小于5&);
if(keyword.length()&30) {
return ErrorResult(&关键词长度不能大于30&);
if(NotLegalString(keyword)) {
return ErrorResult(&搜索范围太大&);
String[] keyfilter = filterk.split(&\\|&);
for(int i=0;i
编译运行,提交这样的地址,就会返回json数据,接下来只需写一个前端页面,解析并显示这些结果即可.
终于全都完成了,我在把狐朋狗友加基友加妹纸在社工库里查了个遍之后,心满意足的睡觉去了,梦中,我经过一条小河上的独木桥,刚走到桥正中,突然腰带一松,裤子掉进了河里,我正在发愁的时候,河面上冒出了一个猥琐的河神,手中拿着三件东西笑眯眯的问我:
“善良的少年哟! 你掉的,是这条金裤子,还是这条银裤子,还是这个装满了裤子的硬盘呢?”
“哈哈哈哈哈哈哈…..”
我对着河神哈哈大笑,笑着笑着,猛然从梦中醒来,睁眼一看,已经天光大亮了,回头看床头时钟,妈蛋! 上班要迟到了!!!…..
ps: banish是女神经!
*作者b41k3r,本文属FreeBuf原创奖励计划,未经许可禁止转载
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致}

我要回帖

更多关于 solr 多个core查询 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信