当前位置: 首页 > solr, 搜索 > 正文

Solr实际案例

关键字:
1 星2 星3 星4 星5 星 (2 次投票, 评分: 5.00, 总分: 5)
Loading ... Loading ...
baidu_share

重写公司的站内搜索。经过前期一段时间对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了解的深入,我能更好的驾驭它了。

本文固定链接: http://www.chepoo.com/solr-apply-example.html | IT技术精华网

Solr实际案例:等您坐沙发呢!

发表评论