`
suichangkele
  • 浏览: 198092 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

solr中的filterCache使用场景源码解读

    博客分类:
  • solr
阅读更多

都知道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除了上面的功能外,还有一个功能就是满足不带有得分的排序时的请求的功能,不过这个功能用到的可能性很小。

 

 

 

 

 

 

分享到:
评论

相关推荐

    solr中cache综述

    1. **solr.search.LRUCache**:基于LRU(Least Recently Used,最近最少使用)算法实现,适用于需要根据访问频率淘汰缓存项的场景。LRUCache会根据最近的访问记录来决定哪些项应该被保留,哪些项应该被淘汰。 - **...

    solr-6.2.0源码

    源码中包含了丰富的注释和示例,帮助开发者深入理解Solr的设计思想和实现细节。 总结来说,Solr 6.2.0是一个强大的全文搜索引擎,它的分布式特性、实时性以及丰富的功能使得它成为企业级搜索应用的理想选择。通过...

    solr4.3源代码一

    Solr4.3源代码的获取通常是为了深入理解其内部工作原理,进行定制开发或优化。在你提供的信息中,我们重点关注的是"lucene"目录,这表明你关注的是Solr中与Lucene搜索引擎库相关的部分。 **Lucene简介** Lucene是...

    Solr 4.0 源代码实例

    源代码实例是学习Solr内部工作原理和自定义功能的关键资源,尤其对于开发者而言,深入理解源码有助于提升系统的优化和扩展能力。 在Solr 4.0版本中,`solr.xml`是Solr的核心配置文件,它定义了Solr实例的基本设置,...

    easynet.solr开发与使用

    在实际开发过程中,Easynet.Solr还提供了完整的解决方案文件(EasyNet.Solr.sln),这包含了项目的全部源码和配置,方便开发者进行二次开发和定制。同时,ClientDemo示例项目展示了如何使用Easynet.Solr进行基本操作...

    solr中文解析器以及使用文档

    3. **配置Solr的中文解析器**:在Solr中使用IK Analyzer,需要在solrconfig.xml和schema.xml这两个配置文件中进行设置。首先,需要在solrconfig.xml中指定查询分析器和索引分析器为IK Analyzer;然后,在schema.xml...

    JAVA+Solr分词项目工程实例源代码学习

    通过这个实例,学习者不仅可以掌握Java编程基础,还能深入理解Solr的工作原理,以及如何在实际项目中集成和使用Solr进行分词和搜索。同时,实践过程中可能会涉及的其他技术包括Maven或Gradle构建工具、日志记录、...

    solr(solr-9.0.0-src.tgz)源码

    通过深入研究`solr-9.0.0-src.tgz`源码,开发者可以理解Solr的工作原理,定制自己的搜索解决方案,解决特定场景下的性能挑战,并为社区贡献新的功能和优化。同时,这也为学习和研究信息检索、全文搜索、分布式计算等...

    Solr(Cloudera)使用手册

    2. **使用场景**:不同的使用场景适合不同类型的缓存。例如,经常重复执行的查询适合使用QueryResultCache。 3. **配置介绍**:在solrconfig.xml文件中配置缓存策略。 - 示例配置: ```xml ...

    solr4.6.0 源代码

    通过研究Solr 4.6.0的源代码,开发者可以深入理解其内部工作原理,定制化搜索服务,优化性能,以及解决在部署和使用过程中遇到的问题。这不仅有助于提升对全文检索技术的理解,也有利于开发更加高效和个性化的搜索...

    solr中文分词器

    1. **下载与安装**:首先,从官方仓库或者源代码仓库下载IK Analyzer的最新版本,如IK Analyzer 2012FF_hf1,并将其解压到合适的目录。 2. **配置Solr schema.xml**:在Solr的`conf`目录下,找到`schema.xml`文件,...

    solr的安装使用步骤

    ### Solr的安装使用步骤详解 #### Solr概述与特性 **Solr** 是 Apache 下的一个顶级开源项目,它基于 **Lucene** 进行构建,提供了强大的全文搜索能力。相较于 Lucene,Solr 提供了更为丰富的查询语言支持,并且...

    solr5的ik中文分词器源码

    解压后的源码需要通过Maven或其他构建工具(如Gradle)进行编译和打包,生成的JAR文件才能被Solr识别并使用。这通常涉及`mvn clean package`命令。 8. **测试与调试**: 源码中的测试用例可以帮助理解分词器的...

    ikanalyzer-solr中文分词包兼容solr7.5

    在Solr的Schema设计中,可以为需要分词的字段指定`ikanalyzer`作为分析器,这样在索引和查询时,Solr就会使用ikanalyzer进行分词操作。 总之,ikanalyzer-solr中文分词包为Solr提供了一种强大的中文处理能力,使得...

    solr6.6.0源码

    这个源码包包含了 Solr 的所有源代码,对于理解 Solr 的工作原理、进行二次开发或者定制化配置有着非常重要的意义。接下来,我们将深入探讨 Solr 6.6.0 中的一些关键知识点。 一、Solr 架构与组件 Solr 的核心架构...

    solr中文文档.pdf

    ### SOLR中文文档知识点概述 ...这些知识点对于初学者来说是非常宝贵的资源,可以帮助他们快速上手SOLR并应用于实际项目中。此外,对于想要深入了解SOLR内部机制的开发者而言,掌握Lucene原理也是十分必要的。

    solr介绍及使用.docx

    Solr 是一个强大的开源全文搜索引擎,基于 Java 语言开发,其核心技术是 Apache Lucene。作为一款搜索服务器,Solr ...通过合理的配置和扩展,Solr 能够适应各种复杂的应用场景,成为企业信息系统中的重要组成部分。

    solr在java中使用总结

    通过以上步骤,我们可以成功地部署Solr并在Java应用程序中使用SolrJ进行数据索引和搜索。Solr的强大功能和灵活性使其成为构建高效搜索系统的一个优秀选择。无论是对于企业级应用还是个人项目,Solr都是一个值得深入...

Global site tag (gtag.js) - Google Analytics