都知道solr有四个缓存,queryResultCache,documentCache,filterCache,fieldValueCache,今天我要好好说一下filterCache,据说他是用来缓存fq的docid的,也就是当搜索到一个fq对应的query的所有的docid之后,对这个结果进行缓存,方便以后的重复使用,这样就能省去更多的io操作。为了得到一个更准确的结论,我就又仔细的读了一遍代码,用公司的4.10.4的版本的solr做了个solr节点,做了很多实验,算是掌握了filterCache的使用场景了吧。
什么情况下会用到filterCache。在SolrIndexSearcher的getDocListC中,如果命中了遇到了缓存(这里的缓存指得是queryResultCache),我们看下代码:
if (queryResultCache != null && cmd.getFilter() == null && (flags & (NO_CHECK_QCACHE | NO_SET_QCACHE)) != ((NO_CHECK_QCACHE | NO_SET_QCACHE))) //如果可以查询缓存 key = new QueryResultKey(q, cmd.getFilterList(), cmd.getSort(), flags);//构建从queryResultCache中查询的key if ((flags & NO_CHECK_QCACHE) == 0) { //再判断一遍如果可以查询缓存 superset = queryResultCache.get(key);//从queryResultCache中查询 if (superset != null) {//缓存命中, if ((flags & GET_SCORES) == 0 || superset.hasScores()) {//如果此次查询不需要返回得分,或者缓存的结果中有得分,则进入if out.docList = superset.subset(cmd.getOffset(), cmd.getLen());//从缓存的结果中取得本次需要的结果集合,判断的根据是start + rows这两个参数,经过这个步骤,可能会因为符合start + rows而又结果,也可能因为不符合而没有结果 } } if (out.docList != null) {//如果有结果, if (out.docSet == null && ((flags & GET_DOCSET) != 0)) {//关键就是这个flags ,我们需要知道这个flats和GET_DOCSET的关系,经过我的代码查找,当使用到了facet的时候,flags & GET_DOCSET) != 0成立,也就是当facet的时候回需要返回docSet if (cmd.getFilterList() == null) {//如果此次查询没有fq out.docSet = getDocSet(cmd.getQuery()); // 这个方法就是获得q解析的query的docSet,先从filterCache中查找,如果没有命中就会再从lucene中查找,然后放入filterCache中。从这里看filteCache也是会放入q的docset的。 } else {//如果有fq List<Query> newList = new ArrayList<>(cmd.getFilterList().size() + 1); newList.add(cmd.getQuery()); newList.addAll(cmd.getFilterList()); out.docSet = getDocSet(newList);// 这个方法也会从filterCache中获取docSet,是在getPositiveDocSet方法里面调用的,然后再在这个方法里面做交集或者差集 ,经过这个方法后,q和fq对应的所有的query的的docSet都会进入到filterCache中 } } return; } }
(我先说一下我是怎么找到facet会设置flags & GET_DOCSET != 0的,在org.apache.solr.search.SolrIndexSearcher.QueryCommand.setNeedDocSet(boolean)方法里面就会将flags设置为flags & GET_DOCSET != 0,而这个方法的调用时在org.apache.solr.handler.component.ResponseBuilder.getQueryCommand()里面,而使用的参数就是org.apache.solr.handler.component.ResponseBuilder.isNeedDocSet(),我们看一下org.apache.solr.handler.component.ResponseBuilder.setNeedDocSet(boolean)这个方法,他的调用实在org.apache.solr.handler.component.FacetComponent.prepare(ResponseBuilder)里面,而且传入的就是true,也就是在打开facet的时候,就会是flags & GET_DOCSET != 0)
上面的代码说明,如果命中了缓存且开启了facet,那么就会调用getDocSet方法,参数或者是一个query,另一个是List<query>,来获得所有的docid以实现facet的功能。在只有一个参数的getDocSet方法里面就会从filterCache中查找docset,如果没有查找就会调用getDocSetNC(NC表示not cache)从lucne的索引中查找,然后放入到filterCache中去,此时q的query的docSet就会被放入fitlerCache了;而在参数是List<query>的方法中也会从filterCache中查找,只不过他是将query单独查找的filterCache(具体的实现方法是getProcessedFilter,这个方法会通过调用getPositiveDocSet从filterCache中获取docSet,然后再在这个方法里面做交集或者差集,这个方法的第二个参数的所有的query的倒排表都会放入到filteCache中去),此时所有的fq的docSet以及q的docSet都被放到了filterCache中去。这就说明了在命中了缓存(再次强调这里的缓存是queryResultCache)的情况下,如果开启了facet,就会从filterCache中查找docSet,并且所有的fq以及q形成的query的docSet都会放入到filterCache中去(从这一点可以发现叫做filterCache不太合适啊,因为q的docSet也会放进去)。
如果没有命中缓存呢,代码是solrIndexSearcher的getDocListC的一部分,如下:
if (useFilterCache) {//先不用管这个,后面会有单独的说明 // now actually use the filter cache. // for large filters that match few documents, this may be // slower than simply re-executing the query. if (out.docSet == null) { out.docSet = getDocSet(cmd.getQuery(), cmd.getFilter()); DocSet bigFilt = getDocSet(cmd.getFilterList()); if (bigFilt != null) out.docSet = out.docSet.intersection(bigFilt); } // todo: there could be a sortDocSet that could take a list of // the filters instead of anding them first... // perhaps there should be a multi-docset-iterator sortDocSet(qr, cmd); } else { // do it the normal way... 也就是从lucene中查找。 if ((flags & GET_DOCSET) != 0) {//还是先判断是不是GET_DOCSET,从上面我们知道,如果是facet的话,就是true,否则是false. // this currently conflates returning the docset for the base query vs the base query and all filters. DocSet qDocSet = getDocListAndSetNC(qr, cmd);//这个方法中,同样会调getProcessedFilter方法,第二个参数是所有的fq的queyr,即fq的所有的docSet都放入了fitlerCache。 if (qDocSet != null && filterCache != null && !qr.isPartialResults())//当没有filter的时候,也会把query对应的docSet放入filteCache。因此此时获得的docSet和query是匹配的。 filterCache.put(cmd.getQuery(), qDocSet); } else { getDocListNC(qr, cmd);//在不进行facet的情况下,对于fq,也会用到上面的getProcessedFilter方法,也就是也会向filterCache中查找,如果没有命中就从lucene中查找,然后将结果放入filterCache。 } }
上面的两个方法,getDocListAndSetNC和getDocListNC里面都会调用getProcessedFilter方法,传入的参数是fq所代表的query,获得的结果就是所有的fq的交集,也就是对于fq,即使是在facet不打开的时候,进行fq的倒排表的合并也是会使用filterCache的。这就说明了在没有命中QueryResultCache的情况下,不论是不是打开facet也会使用filterCache的,使用它进行fq的倒排表的合并,不过在使用facet的时候对于docSet的获得仍然是通过先查询的lucene(因为没有命中缓存嘛)。
经过上面的代码,无论是命中缓存还是不命中缓存的时候,我们可以总结一个结论,filterCache的作用有两个,一个是进行倒排表的合并,是实现了多个fq的交集,第二个就是从filterCache中获得docset,实现facet的功能。或者更抽象一下,filterCache就是存贮query的docSet的,query不一定非得是fq,q的倒排表也会放入的。
其实filterCache还有一个功能,也就是上面代码中的if(useFilterCache)的部分,他的逻辑很简单,我们看一下代码
boolean useFilterCache = false; if ((flags & (GET_SCORES | NO_CHECK_FILTERCACHE)) == 0 && useFilterForSortedQuery && cmd.getSort() != null && filterCache != null) {//如果这次请求是不用返回得分的,且在solrconfig中配置了useFilterForSortedQuery=true且这次请求有排序且filterCache不是null useFilterCache = true; SortField[] sfields = cmd.getSort().getSort(); for (SortField sf : sfields) { if (sf.getType() == SortField.Type.SCORE) {//如果所有的排序中没有使用score的 useFilterCache = false; break; } } } if (useFilterCache) {//下面的代码就是使用filerCache实现请求的结果 if (out.docSet == null) { out.docSet = getDocSet(cmd.getQuery(), cmd.getFilter());//这个是从lucene的索引中查找query + cmd.getFilter的倒排表的docSet(注意这里的cmd.fitler不是fq,fq是cmd.getFilterList) DocSet bigFilt = getDocSet(cmd.getFilterList());//从filterCache中查找,如果没有找得到则从lucene中查找再放入 if (bigFilt != null) out.docSet = out.docSet.intersection(bigFilt);//两者取交集 } sortDocSet(qr, cmd);//对结果结合进行排序 } else {xxxxx}//同上,省略
为什么上面要单独强调不能使用得分呢?原因很简单,因为如果使用得分排序的话,就可能需要tf,可能需要位置信息,可能需要payload,但是filterCache中是没有这些的,他仅仅含有id,所以如果使用score的话,就不能使用fitlerCache了。而如果不适用score排序的话,也就是使用某个域或者某个函数排序,这样就可以根据id从FieldCache中去查找了,此时filterCache提供的id就可以满足需求。
所以,从上所述,filterCache除了上面的功能外,还有一个功能就是满足不带有得分的排序时的请求的功能,不过这个功能用到的可能性很小。
相关推荐
1. **solr.search.LRUCache**:基于LRU(Least Recently Used,最近最少使用)算法实现,适用于需要根据访问频率淘汰缓存项的场景。LRUCache会根据最近的访问记录来决定哪些项应该被保留,哪些项应该被淘汰。 - **...
源码中包含了丰富的注释和示例,帮助开发者深入理解Solr的设计思想和实现细节。 总结来说,Solr 6.2.0是一个强大的全文搜索引擎,它的分布式特性、实时性以及丰富的功能使得它成为企业级搜索应用的理想选择。通过...
Solr4.3源代码的获取通常是为了深入理解其内部工作原理,进行定制开发或优化。在你提供的信息中,我们重点关注的是"lucene"目录,这表明你关注的是Solr中与Lucene搜索引擎库相关的部分。 **Lucene简介** Lucene是...
源代码实例是学习Solr内部工作原理和自定义功能的关键资源,尤其对于开发者而言,深入理解源码有助于提升系统的优化和扩展能力。 在Solr 4.0版本中,`solr.xml`是Solr的核心配置文件,它定义了Solr实例的基本设置,...
在实际开发过程中,Easynet.Solr还提供了完整的解决方案文件(EasyNet.Solr.sln),这包含了项目的全部源码和配置,方便开发者进行二次开发和定制。同时,ClientDemo示例项目展示了如何使用Easynet.Solr进行基本操作...
3. **配置Solr的中文解析器**:在Solr中使用IK Analyzer,需要在solrconfig.xml和schema.xml这两个配置文件中进行设置。首先,需要在solrconfig.xml中指定查询分析器和索引分析器为IK Analyzer;然后,在schema.xml...
通过这个实例,学习者不仅可以掌握Java编程基础,还能深入理解Solr的工作原理,以及如何在实际项目中集成和使用Solr进行分词和搜索。同时,实践过程中可能会涉及的其他技术包括Maven或Gradle构建工具、日志记录、...
2. **使用场景**:不同的使用场景适合不同类型的缓存。例如,经常重复执行的查询适合使用QueryResultCache。 3. **配置介绍**:在solrconfig.xml文件中配置缓存策略。 - 示例配置: ```xml ...
通过深入研究`solr-9.0.0-src.tgz`源码,开发者可以理解Solr的工作原理,定制自己的搜索解决方案,解决特定场景下的性能挑战,并为社区贡献新的功能和优化。同时,这也为学习和研究信息检索、全文搜索、分布式计算等...
通过研究Solr 4.6.0的源代码,开发者可以深入理解其内部工作原理,定制化搜索服务,优化性能,以及解决在部署和使用过程中遇到的问题。这不仅有助于提升对全文检索技术的理解,也有利于开发更加高效和个性化的搜索...
1. **下载与安装**:首先,从官方仓库或者源代码仓库下载IK Analyzer的最新版本,如IK Analyzer 2012FF_hf1,并将其解压到合适的目录。 2. **配置Solr schema.xml**:在Solr的`conf`目录下,找到`schema.xml`文件,...
### Solr的安装使用步骤详解 #### Solr概述与特性 **Solr** 是 Apache 下的一个顶级开源项目,它基于 **Lucene** 进行构建,提供了强大的全文搜索能力。相较于 Lucene,Solr 提供了更为丰富的查询语言支持,并且...
解压后的源码需要通过Maven或其他构建工具(如Gradle)进行编译和打包,生成的JAR文件才能被Solr识别并使用。这通常涉及`mvn clean package`命令。 8. **测试与调试**: 源码中的测试用例可以帮助理解分词器的...
在Solr的Schema设计中,可以为需要分词的字段指定`ikanalyzer`作为分析器,这样在索引和查询时,Solr就会使用ikanalyzer进行分词操作。 总之,ikanalyzer-solr中文分词包为Solr提供了一种强大的中文处理能力,使得...
这个源码包包含了 Solr 的所有源代码,对于理解 Solr 的工作原理、进行二次开发或者定制化配置有着非常重要的意义。接下来,我们将深入探讨 Solr 6.6.0 中的一些关键知识点。 一、Solr 架构与组件 Solr 的核心架构...
### SOLR中文文档知识点概述 ...这些知识点对于初学者来说是非常宝贵的资源,可以帮助他们快速上手SOLR并应用于实际项目中。此外,对于想要深入了解SOLR内部机制的开发者而言,掌握Lucene原理也是十分必要的。
Solr 是一个强大的开源全文搜索引擎,基于 Java 语言开发,其核心技术是 Apache Lucene。作为一款搜索服务器,Solr ...通过合理的配置和扩展,Solr 能够适应各种复杂的应用场景,成为企业信息系统中的重要组成部分。
通过以上步骤,我们可以成功地部署Solr并在Java应用程序中使用SolrJ进行数据索引和搜索。Solr的强大功能和灵活性使其成为构建高效搜索系统的一个优秀选择。无论是对于企业级应用还是个人项目,Solr都是一个值得深入...