Solr在Lucene之上开发了很多Cache功能,从目前提供的Cache类型有:
(1)filterCache
(2)documentCache
(3)fieldvalueCache
(4)queryresultCache
而每种Cache针对具体的查询请求进行对应的Cache。本文将从几个方面来阐述上述几种Cache在Solr的运用,具体如下:
(1)Cache的生命周期
(2)Cache的使用场景
(3)Cache的配置介绍
(4)Cache的命中监控
1 Cache生命周期
所有的Cache的生命周期由SolrIndexSearcher来管理,如果Cache对应的SolrIndexSearcher被重新构建都代表正在运行的Cache对象失效,而SolrIndexSearcher是否重新打开主要有几个方面影响。
(1)增量数据更新后提交DirectUpdateHandler2.commit(CommitUpdateCommand cmd),该方法代码如下:
其中最重要的方法
再展开来看参数含义,
参数1 boolean forceNew,是否打开新的searcher对象
参数2 boolean returnSearcher,是否返回最新的searcher对象
参数3 final Future[] waitSearcher 是否等待searcher的预加工动作,也就是调用该方法的线程将会等待这个searcher对象的预加工动作,如果该searcher对象管理很多的 Cache并设置较大的预热数目,该线程将会等待较长时间才能返回。(预热,也许会很多人不了解预热的含义,我在这里稍微解释下,例如一个Cache已经 缓存了比较多的值,如果因为新的IndexSearcher被重新构建,那么新的Cache又会需要重新累积数据,那么会发现搜索突然会在一段时间性能急 剧下降,要等到Cache重新累计了一定数据,命中率才会慢慢恢复。所以这样的情形其实是不可接受的,那么我们可以做的事情就是将老Cache对应的 key,在重新构建SolrIndexSearcher返回之前将这些已经在老Cache中Key预先从磁盘重新load Value到Cache中,这样暴露出去的SolrIndexSearcher对应的Cache就不是一个内容为空的Cache。而是已经“背地”准备好 内容的Cache)
getSearcher()关于Cache有2个最重要的代码段,其一,重新构造新的SolrIndexSearcher:
在看看创建SolrIndexSearcher构造函数关于Cache的关键代码:
其二,将老searcher对应的Cache进行预热:
展开看warm(SolrIndexSearcher old)方法(具体如何预热Cache将在其他文章进行详述):
到这里为止,SolrIndexSearcher进行Cache创建就介绍完毕,而Cache的销毁也是通过SolrIndexSearcher的关闭一并进行,见solrIndexSearcher.close()方法:
OK,到这里,Cache经由SolrIndexSearcher管理的逻辑就完整介绍完毕。
2 Cache的使用场景
(1)filterCache
该Cache主要是针对用户Query中使用fq的情况,会将fq对应的查询结果放入Cache,如果业务上有很多比较固定的查询Query,例如固定状 态值,比如固定查询某个区间的Query都可以使用fq将结果缓存到Cache中。查询query中可以设置多个fq进行Cache,但是值得注意的是多 个fq都是以交集的结果返回。
另外一个最为重要的例外场景,在Solr中如果设置,useFilterForSortedQuery=true,filterCache不为空,且带有sort的排序查询,将会进入如下代码块:
(2)documentCache主要是对document结果的Cache,一般而言如果查询不是特别固定,命中率将不会很高。
(3)fieldvalueCache 缓存在facet组件使用情况下对multiValued=true的域相关计数进行Cache,一般那些多值域采用facet查询一定要开启该Cache,主要缓存(参考UnInvertedField 的实现):
maxTermCounts 最大Term数目
numTermsInField 该Field有多少个Term
bigTerms 存储那些Term docFreq 大于threshold的term
tnums 一个记录 term和何其Nums的二维数组
每次FacetComponent执行process方法-->SimpleFacets.getFacetCounts()-->getFacetFieldCounts()-->getTermCounts(facetValue)-->
UnInvertedField.getUnInvertedField(field, searcher);展开看该方法
(4)queryresultCache 对Query的结果进行缓存,主要在SolrIndexSearcher类的getDocListC()方法中被使用,主要缓存具有 QueryResultKey的结果集。也就是说具有相同QueryResultKey的查询都可以命中cache,所以我们看看 QueryResultKey的equals方法如何判断怎么才算相同QueryResultKey:
从上面的代码看出,如果要命中一个queryResultCache,需要满足query、filterquery sortFiled一致才行。
3 Cache的配置介绍
要使用Solr的四种Cache,只需要在SolrConfig中配置如下内容即可:
其中size为缓存设置大小,initalSize初始化大小,autowarmCount 是最为关键的参数代表每次构建新的SolrIndexSearcher的时候需要后台线程预热加载到新Cache中多少个结果集。
那是不是这个预热数目越大就越好呢,其实还是要根据实际情况而定。如果你的应用为实时应用,很多实时应用的实现都会在很短的时间内去得到重新打开的 内存索引indexReader,而Solr默认实现就会重新打开一个新的SolrIndexSearcher,那么如果Cache需要预热的数目越多, 那么打开新的SolrIndexSearcher就会越慢,这样对实时性就会大打折扣。
但是如果设置很小。每次都打开新的SolrIndexSearcher都是空Cache,基本上那些fq和facet的查询就基本不会命中缓存。所以对实时应用需要特别注意。
4 Cache的命中监控
页面查询:
http://localhost:8080/XXXX/XXXX/admin/stats.jsp 进行查询即可:
其中 lookups 为当前cache 查询数, hitratio 为当前cache命中率,inserts为当前cache插入数,evictions从cache中踢出来的数据个数,size 为当前cache缓存数, warmuptime为当前cache预热所消耗时间,而已cumulative都为该类型Cache累计的查询,命中,命中率,插入、踢出的数目。