Solr之困
http://www.kafka0102.com/2010/08/319.html重写公司的站内搜索。经过前期一段时间对lucene和solr的熟悉,最后决定使用Solr作为新系统的基础框架。现在已经是第一阶段开发的后期,核心代码行数有11000+(不包含admin及client等)。现已实现的功能要比已有系统要丰富些,但综合比较两个系统总的代码量,其实新系统并不多得太多。新系统使用Solr代替了已有系统实现的部分功能,这减少了新系统的代码量,同是新系统实现了已有系统不具有的功能,也增加了一些代码量。开发的这段时间,因为新系统中很多代码是独立于Solr的,所以和Solr的交互也是时断时续,以使得即便到了开发后期我还能发现Solr实现的一些细节带给我的困扰。
抛开我所做的系统来说,如果要选择一个站内搜索解决方案,Solr在某些场景下可能是个很不错的选择。因为Solr提供了Web server支持通过Http来更新索引、重建索引、查询等功能,如果需求对上Solr,甚至可以不需要基于Solr做二次开发就直接满足需要。多美妙的事情阿。不过,如果你需要些高级功能,那么可能你需要基于Solr做些工作了。比如,如果索引库很大,可以将索引库拆成多个shard,查询时对多个shard进行,这个功能Solr是支持的;不过,建索引的事情就需要自己搞定了,比如在Solr前面加个Proxy(或者只是个库函数),在建索引时根据特定的策略提交到不同的shard上。这个其实也还好了,但如果我需要一个涉及到多个索引库(各索引库有不同的schema)的查询,比如要做整站搜索,那么Solr的shard查询就用不上了,因为它必须要求各shard的schema一致。而我要做的实际是个通用搜索,这样的问题就有些接踵而至了。尽管和Solr磨合的过程花了不少时间,涉及到对它提供的功能、设计、源码的理解等等,并且有时还要妥协它开发,有时还要舍弃它已实现的功能而另起炉灶。但不可否认的是,对于初涉站内搜索开发的我来说,使用Solr并不是太坏的选择,从中也学到了Solr优秀的地方,同时也看到它不足的地方,都是收获。本文会简单的总结下个人在应用Solr过程中一些不是很爽的地方,爽的地方姑且按下不表。
Solr实现上有个核心东西,就是SolrCore。每个SolrCore对应着一个索引库,几乎所有的操作都是针对单个SolrCore进行的,似乎Solr的初衷就是如此,并没有考虑到多个SolrCore之间的关联。所以,可以看到的是,每个SolrQueryRequest都会关联到一个SolrCore,SolrRequestHandler的获得也是从SolrCore取得的。这糟糕的设计使得,当需要对多个SolrCore做管理时,Solr不得不做出CoreAdminHandler,它虽然实现了SolrRequestHandler接口,但它是脱离于SolrCore的,使得配置上也和其他handler不一样。而Solr的shard查询的支持就更糟糕,它要求shard的SolrCore的schema都是一致的,而不能查询异构的SolrCore。为了解决这个问题,我在Solr基础上加了个VirtualCore(这个概念现在看起来不是很好,或许IndexCore会更好些),VirtualCore里面可以包含一个或多个SolrCore,而很多操作就不是针对SolrCore而是针对VirtualCore了。比如索引库index被拆分成index.0、index.1、index.2,无论索引还是查询,客户端只需要向系统针对core=index进行请求,无需关心index被系统拆分成几个库,这些库被如何分布,系统会通过配置把这些事情做好。对于整站多个库的联合查询,就是针对多个VirtualCore进行,可以通过配置指定各个VirtualCore的请求参数而不需要像Solr那样有严格的约束。
引入了VirtualCore,使得Solr的一些实现不能得手的使用上。首当其冲的就是它的SearchHandler,我不得不在它的基础上重写了一个,它的shard请求异常处理策略也很有问题,如果shard请求中的某个出现异常,它就不会返回结果,这样做的好处是保证返回结果的全局准确性,但却降低了可用性。这里也需要考虑到查询结果cache的问题,如果在Solr前面加了查询结果Cache,那么Solr这种准确性要求就是有必要的。但在我的实现中,是可以有多少shard返回就处理多少,但在异常的情况下就不做查询结果cache处理。
VirtualCore也使得Solr强悍的DIH也用不上了,但即便没有VirtualCore,DIH也很难解决单点提交多个shard索引的问题。DIH直接对索引的SolrCore做重建索引处理,并没有对重建索引过程提供灵活的hook(尽管它确实提供了一些hook)。就我的需求来说,我希望每索引一个文档同时会根据一定的策略来更新摘要数据库,我浏览了DIH的文档和代码,似乎很难做到。而且,DIH是直接在现有索引上做重建的,如果重建时间很长或者出现问题,使得同时进来的更新索引被阻塞,就会影响到正常的服务。
Solr对配置文件的把握上也不够好。Solr对solrconfig.xml文件提供了Java属性值替换配置文件变量,但solr.xml却没有支持,使得线上线下配置文件中充斥着不同的绝对路径。也有好的一方面,比如schema.xml支持Xinclude,使得多个索引库的schema.xml可以共用相同的field type定义。不过,如果多个索引库的schema能集中在一个文件而不是散落成多个文件,管理起来会更方便。这样的问题同时也存在于solrconfig.xml,尽管solrconfig.xml大多数项的配置都是通用的,不过多个索引库时,searcher的warm请求参数可能就会不一样,这使得我在考虑安排时间改写它的默认Lisnter的实现。
Solr的索引复制有一个细节,那就是master和slave保持长连接,master通过调用OutputStream的flush方法不断把数据发送给slave,如果使用Servlet容器,通过Servlet得到OutputStream这样做没什么问题,但如果使用Netty作为服务器框架,并且使用Netty的http实现,那就实现不了这个效果。这使得我不得不放弃Netty改用Jetty了。
再回到查询上,Solr的SearchHandler只会得到doc id list,而不会得到需要的所请求的字段内容,它是在ResponseWriter输出时根据doc id从IndexReader得到需要的字段。在我的设计中,索引只会存储逻辑主键id,得到逻辑主键id后再从另外的摘要库把其他字段取回(或者就是返回id列表给客户端),但我显然需要在ResponseWriter输出前做完这些事情,这使得我并不得不修改request需要返回的字段列表为空。而这个ResponseWriter是需要和SolrCore的schema绑定的,结果对于并不存在的VirtualCore,我还不得不使用上配置为空并且没有索引的fake schema蒙混过去。
还是关于配置,Solr复制slave端配置的master url需要指定参数core,这使得每个SolrCore都有不同的master url而不能共用一个solrconfig.xml,而我真的很希望它们能共用一个solrconfig.xml。其实这个core参数在ReplicationHandler中完全可以得到,Solr没这么做的一个可能的原因是,它支持的请求url格式是http://host/corename/qt?xx=dd,把corename作为url path的一部分让我用起来很不爽,所以我把请求的格式格式改成:http://host/qt?core=aaa&xx=dd,并出下策把Solr和复制相关的代码拷过来,增加了几行代码完事。
问题当然还有,但就像上面提到的,遇到问题总要找个解决方案,尽管有的方案看起来有些二。在回想上面提到的问题之后,我对现在完成的产出的可用性有些怀疑,我到现在还没有完整的测试过这个系统,所以,它还需要我更仔细的打磨。值得庆幸的是,随着对Solr了解的深入,我能更好的驾驭它了。
==================== 华丽的终止符 ===================
Solr之困的更多相关文章
- solr服务中集成IKAnalyzer中文分词器、集成dataimportHandler插件
昨天已经在Tomcat容器中成功的部署了solr全文检索引擎系统的服务:今天来分享一下solr服务在海量数据的网站中是如何实现数据的检索. 在solr服务中集成IKAnalyzer中文分词器的步骤: ...
- Solr 排除查询
前言 solr排除查询也就是我们在数据库和程序中经常处理的不等于,solr的语法是在定语前加[-].. StringBuilder sbHtml=new StringBuilder(); shBhtm ...
- Solr高级查询Facet
一.什么是facet solr种以导航为目的的查询结果成为facet,在用户查询的结果上根据分类增加了count信息,然后用户根据count信息做进一步搜索. facet主要用于导航实现渐进式精确搜索 ...
- [Solr] (源) Solr与MongoDB集成,实时增量索引
一. 概述 大量的数据存储在MongoDB上,需要快速搜索出目标内容,于是搭建Solr服务. 另外一点,用Solr索引数据后,可以把数据用在不同的项目当中,直接向Solr服务发送请求,返回xml.js ...
- sorl6.0+jetty+mysql搭建solr服务
1.下载solr 官网:http://lucene.apache.org/solr/ 2.目录结构如下 3.启动solr(默认使用jetty部署) 在path路径下将 bin文件夹对应的目录加入,然后 ...
- Solr Facet 默认值
前言 今天在用Solr Facet遇到了默认值的问题,我用Facet.field查询发现数据总共100条,刚开始没有注意,发现少个别数据,但是用这几个个别的id查询又能查出来数据.才发现是Facet默 ...
- solr添加多个core
在D:\solr\solr_web\solrhome文件夹下: 1)创建core0文件夹 2)复制D:\solr\solr_web\solrhome\configsets\basic_configs/ ...
- solr定时更新索引遇到的问题(SolrDataImportProperties Error loading DataImportScheduler properties java.lang.NullPointerException)
问题描述 报如下错误,很显然,问题原因:空指针异常: ERROR (localhost-startStop-1) [ ] o.a.s.h.d.s.SolrDataImportProperties ...
- Solr实战:使用Hue+Solr实现标签查询
公司最近在研究多条件组合查询方案,Google的一位技术专家Sam和我们讨论了几个备选方案. Sam的信: 我做了进一步研究,目前有这么几种做法: 1) 最直接粗暴,只做一个主index,比如按行业+ ...
随机推荐
- linux基础命令学习(一)系统的关机、重启以及注销
1.shutdown shutdown 参数说明: [-t] 在改变到其它runlevel之前﹐告诉init多久以后关机. [-r] 重启计算器[reboot]. [-k] 并不真正关机﹐只是送警告信 ...
- WebDriver元素查找方法摘录与总结
WebDriver元素查找方法摘录与总结 整理By:果冻迪迪 selenium-webdriver提供了强大的元素定位方法,支持以下三种方法. • 单个对象的定位方法 • 多个对象的定位方法 • 层级 ...
- 深入浅出JDBC-操作时间与大对象(Clob/Blob)
一.时间(Date.Time.Timestamp) java.sql.Date/java.sql.Time/java.sql.Timestamp extends java.util.Date publ ...
- MariaDB 10 (MySQL DB) 多主复制并实现读写分离
----本文大纲 简介 资源配置 拓扑图 实现过程 ==================== 一.简介 MMM 即Master-Master Replication Manager for MySQL ...
- External components provide true shutdown for boost converter
The step-up switching-converter circuit in Figure 1 presents a familiar problem: If you shut down bo ...
- WICED -- Wireless Internet Connectivity for Embedded Devices
Broadcom's Wireless Internet Connectivity for Embedded Devices (WICED™) platform (pronounced "w ...
- c++多行字符串,可以这么写
c++多行字符串,可以这么写:CString s;s.Format("CREATE TABLE %s(\[ID] [int] IDENTITY(1,1) NOT NULL,\[Vendor] ...
- 在WPF中显示动态GIF
在我们寻求帮助的时候,最不愿意听到的答复是:很抱歉,在当前版本的产品中还没有实现该功能... 在WPF中显示动态的GIF图像时便遇到了这样的问题,WPF中强大的Image控件却不支持动态的GIF(其只 ...
- PHP抓取页面中的邮箱
<?php $url='http://www.cnblogs.com/tinyphp/p/3234926.html'; //当页已留邮箱 $content=file_get_contents($ ...
- PHP:同一件事,有太多的方式
背景 刚接触PHP,发现PHP做一件事有太多的方式,挺灵活的,这或许是PHP的一种设计哲学,也有可能是语言演化的结果,下面举几个例子. 几个例子 在web中嵌入PHP代码. <?php ?&g ...