转自:http://www.blogjava.net/conans/articles/380684.html
本文将介绍Solr查询中涉及到的Cache使用及相关的实现。Solr查询的核心类就是SolrIndexSearcher,
每个core通常在 同一时刻只由当前的SolrIndexSearcher供上层的handler使用
(当切换SolrIndexSearcher时可能会有两个同时提供服务),而Solr的各种Cache是依附于SolrIndexSearcher的,SolrIndexSearcher在则Cache 生,SolrIndexSearcher亡则Cache被清空close掉。
Solr中的应用Cache有filterCache、 queryResultCache、documentCache等,这些Cache都是SolrCache的实现类,
并且是 SolrIndexSearcher的成员变量,各自有着不同的逻辑和使命,下面分别予以介绍和分析。
1、SolrCache接口实现类
Solr提供了两种SolrCache接口实现类:solr.search.LRUCache和solr.search.FastLRUCache。
FastLRUCache是1.4版本中引入的,其速度在普遍意义上要比LRUCache更fast些。
1.1、solr.search.LRUCache
LRUCache可配置参数如下:1)size:cache中可保存的最大的项数,默认是1024
2)initialSize:cache初始化时的大小,默认是1024。
3)autowarmCount:
当切换SolrIndexSearcher时,可以对新生成的SolrIndexSearcher做autowarm(预热)处理。
autowarmCount表示从旧的SolrIndexSearcher中取多少项来在新的SolrIndexSearcher中被重新生成,
如何重新生成由CacheRegenerator实现。在当前的1.4版本的Solr中,这个autowarmCount只能取预热的项数,
将来的4.0版本可以指定为已有cache项数的百分比,以便能更好的平衡autowarm的开销及效果。
如果不指定该参数,则表示不做autowarm处理。实现上,LRUCache直接使用LinkedHashMap来缓存数据,
由initialSize来限定cache的大小,淘汰策略也是使用LinkedHashMap的内置的LRU方式,
读写操作都是对map的全局锁,所以并发性效果方面稍差。1.2、solr.search.FastLRUCache
在配置方面,FastLRUCache除了需要LRUCache的参数,还可有选择性的指定下面的参数:1)minSize:当cache达到它的最大数,淘汰策略使其降到minSize大小,默认是0.9*size。
2)acceptableSize:当淘汰数据时,期望能降到minSize,但可能会做不到,则可勉为其难的降到acceptableSize,
默认是0.95*size。
3)cleanupThread:相比LRUCache是在put操作中同步进行淘汰工作,FastLRUCache可选择由独立的线程来做,
也就是配置cleanupThread的时候。当cache大小很大时,每一次的淘汰数据就可能会花费较长时间,
这对于提供查询请求的线程来说就不太合适,由独立的后台线程来做就很有必要。实现上,
FastLRUCache内部使用了ConcurrentLRUCache来缓存数据,它是个加了LRU淘汰策略的ConcurrentHashMap,
所以其并发性要好很多,这也是多数Java版Cache的极典型实现。
2、filterCache
filterCache存储了无序的lucene document id集合,该cache有3种用途:1)filterCache
存储了filter queries(“fq”参数)得到的document id集合结果。Solr中的query参数有两种,即q和fq。如果fq存在,
Solr是先查询fq(因为fq可以多个,所以多个fq查询是个取结果交集 的过程),之后将fq结果和q结果取并。
在这一过程中,filterCache就是key为单个fq(类型为Query),value为documentid集合(类型为DocSet)的cache。
对于fq为range query来说,filterCache表现出其有价值的一面。
2)filterCache
还可用于facet查询(http://wiki.apache.org/solr/SolrFacetingOverview),facet查询中各
facet的计数是通过对满足query条件的document
id集合(可涉及到filterCache)的处理得到的。因为统计各facet计数可能会涉及到所有的doc
id,所以filterCache的大小需要能容下索引的文档数。
3)如果solfconfig.xml中配置了<useFilterForSortedQuery/>,
那么如果查询有filter(此filter是一需要过滤的DocSet,而不是fq,我未见得它有什么用),
则使用filterCache。
对于是否使用filterCache及如何配置filterCache大小,需要根据应用特点、统计、效果、经验等各方面来评估。
对于使用fq、facet的应用,对filterCache的调优是很有必要的。
4)客户端使用
使用filterQuery之后的查询代码:
SolrQuery query = new SolrQuery(); query.addFilterQuery("status:0 AND biz_type:1 AND class_id:1"); query.setQuery("xxx:123"); QueryResponse response = qyeryServer.query(query);
经过测试这样优化之后,查询的RT会明显减小,QPS会有明显提升。
使用filterquery过程中需要注意点:
●不能在filterQuery 上重复出现query中的查询参数,如果上面的filterquery调用方法如下所示:
- query.addFilterQuery("status:0 AND biz_type:1 AND class_id:1 AND xxx:123");
- query.setQuery("xxx:123");
如上,条件xxx:123 在filterQuery和query上都出现了,这样的写法非但起不到查询优化的目的,而且还会增加查询的性能开销。
●尽量减少调用addFilterQuery方法的次数
query.addFilterQuery("status:0 "); query.addFilterQuery("biz_type:1 "); query.addFilterQuery("class_id:1 "); query.setQuery("xxx:123");
如上,将status:0 AND biz_type:1 AND class_id:1 这个组合查询条件,分三次调用filterQuery方法来完成,这样的调用方法虽然是正确的,并且能起到性能优化的效果,优化性能没有调用一次addFilterQuery方法来得高,原因是多调用了两次addFilterQuery,就意味着最后需要多进行两次结果集的求交运算,虽然结果集求交运算速度很快,但毕竟是有性能损耗的。
不过从内存开销的角度来说,调用三次addfilterQuery方法这样可以有效降低内存的使用量,这个是肯定的。所以在是否调用多次addFilterQuery方法的原则是,在内存开销允许的前提下,将量将所有filterQuery条件,通过调用有限次数的addFilterQuery方法来完成。
3、queryResultCache
顾名思义,queryResultCache是对查询结果的缓存(SolrIndexSearcher中的cache缓存的都是document id set),这个结果就是针对查询条件的完全有序的结果。
因为查询参数是有start和rows的,所以某个QueryResultKey可能命中了cache,但start和rows却不在cache的
document id set范围内。当然,document id
set是越大命中的概率越大,但这也会很浪费内存,这就需要个参数:queryResultWindowSize来指定document id set的大小。
4、documentCache
又顾名思义,documentCache用来保存<doc_id,document>对的。如果使用documentCache,就尽可能开大
些,至少要大过<max_results> *<max_concurrent_queries>,否则因为cache的淘汰,
一次请求期间还需要重新获取document一次。也要注意document中存储的字段的多少,避免大量的内存消耗。
5、User/Generic Caches
Solr支持自定义Cache,只需要实现自定义的regenerator即可
6、The Lucene FieldCache
lucene中有相对低级别的FieldCache,Solr并不对它做管理,所以,lucene的FieldCache还是由lucene的IndexSearcher来搞。
7、autowarm
上面有提到autowarm,autowarm触发的时机有两个,一个是创建第一个Searcher时(firstSearcher),一个是创建个新
Searcher(newSearcher)来代替当前的Searcher。在Searcher提供请求服务前,Searcher中的各个Cache可以
做warm处理,处理的地方通常是SolrCache的init方法,而不同cache的warm策略也不一样。
1)filterCache:filterCache注册了下面的CacheRegenerator,就是由旧的key查询索引得到新值put到新cache中。
2)queryResultCache:queryResultCache的autowarm不在SolrCache的init(也就是说,不是去遍历已
有的queryResultCache中的query key执行查询),而是通过SolrEventListener接口的void
newSearcher(SolrIndexSearcher newSearcher, SolrIndexSearcher
currentSearcher)方法,来执行配置中特定的query查询,达到显示的预热lucene FieldCache的效果。
3)documentCache:因为新索引的document id和索引文档的对应关系发生变化,所以documentCache没有warm的过程,
落得白茫茫一片真干净。尽管autowarm很好,也要注意autowarm带来的开销,这需要在实际中检验其warm的开销,
也要注意Searcher的切换频率,避免因为warm和切换影响Searcher提供正常的查询服务。