solr中Cache综述

一、概述

Solr查询的核心类就是SolrIndexSearcher,每个core通常在同一时刻只由当前的SolrIndexSearcher供上层的handler使用(当切换SolrIndexSearcher时可能会有两个同时提供服务),而Solr的各种Cache是依附于SolrIndexSearcher的,SolrIndexSearcher存在则Cache生,SolrIndexSearcher亡则Cache被清空close掉。Solr在Lucene之上开发了很多Cache功能,从目前提供的Cache类型有:

(1)filterCache

(2)documentCache

(3)fieldvalueCache

(4)queryresultCache

(5)User/GenericCaches

其中1、2、3、4都是SolrCache的实现类,并且是SolrIndexSearcher的成员变量,各自有着不同的逻辑和使命。

二、SolrCache接口实现类

Solr提供了两种SolrCache接口实现类:solr.search.LRUCache和solr.search.FastLRUCache。FastLRUCache是1.4版本中引入的,其速度在普遍意义上要比LRUCache更快些。

下面是对SolrCache接口主要方法的注释:

public interface SolrCache {

/**

*Solr在解析配置文件构造SolrConfig实例时会初始化配置中的各种CacheConfig,

* 在构造SolrIndexSearcher时通过SolrConfig实例来newInstance SolrCache,

* 这会调用init方法。参数args就是和具体实现(LRUCache和FastLRUCache)相关的

* 参数Map,参数persistence是个全局的东西,LRUCache和FastLRUCache用其来统计

*cache访问情况(因为cache是和SolrIndexSearcher绑定的,所以这种统计就需要一个

* 全局的注入参数),参数regenerator是autowarm时如何重新加载cache,

*CacheRegenerator接口只有一个被SolrCache warm方法回调的方法:

*boolean regenerateItem(SolrIndexSearcher newSearcher,

*SolrCache newCache, SolrCache oldCache, Object oldKey, Object oldVal)

*/

public Object init(Map args, Object persistence, CacheRegeneratorregenerator);

/**:TODO: copy from Map */

public int size();

/**:TODO: copy from Map */

public Object put(Object key, Object value);

/**:TODO: copy from Map */

public Object get(Object key);

/**:TODO: copy from Map */

public void clear();

/**

* 新创建的SolrIndexSearcher autowarm方法,该方法的实现就是遍历已有cache中合适的

* 范围(因为通常不会把旧cache中的所有项都重新加载一遍),对每一项调用regenerator的

*regenerateItem方法来对searcher加载新cache项。

*/

void warm(SolrIndexSearcher searcher, SolrCache old) throws IOException;

/**Frees any non-memory resources */

public void close();

2.1、solr.search.LRUCache

LRU又称最近最少使用。假如缓存的容量为10,那么把缓存中的对象按访问(插入)的时间先后排序,当容量不足时,删除时间最早的。Solr中LRUCache是通过LinkedHashMap来实现的。

LRUCache可配置参数如下:

1)size:cache中可保存的最大项数,默认是1024

2)initialSize:cache初始化时容量大小,默认是1024。

3)autowarmCount:当切换SolrIndexSearcher时,可以对新生成的SolrIndexSearcher做autowarm(预热)处理。autowarmCount表示从旧的SolrIndexSearcher中取多少项来在新的SolrIndexSearcher中被重新生成,如何重新生成由CacheRegenerator实现。1.4版本的Solr中,这个autowarmCount只能取预热的项数,如果不指定该参数,则表示不做autowarm处理。

实现上,LRUCache直接使用LinkedHashMap来缓存数据,由initialSize来限定cache的大小,淘汰策略也是使用LinkedHashMap内置的LRU方式,读写操作都是对map的全局锁,所以在高并发条件下,其性能会有所影响。因此Solr用另外一种方式实现了LRUCache,即FastLRUCache。

2.2、solr.search.FastLRUCache

FastLRUCache内部采用了ConcurrentLRUCache实现,而ConcurrentLRUCache内部又采用ConcurrentHashMap实现,所以是线程安全的。缓存通过CacheEntry中的访问标记lastAccessed来维护CacheEntry被访问的先后顺序。 即每当Cache有get或者put操作,则当前CacheEntry的lastAccessed都会变成最大的(state.accessCounter)。当FastLRUCache容量已满时,通过markAndSweep方式来剔除缓存中lastAccessed最小的N个项以保证缓存的大小达到一个acceptable的值。

在配置方面,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的极典型实现。

三 、 Cache原理及配置

3.1 filterCache

该Cache主要是针对用户Query中使用fq(filter query)的情况,会将fq对应的查询结果放入Cache,如果业务上有很多比较固定的查询Query,例如固定状态值,比如固定查询某个区间的Query都可以使用fq将结果缓存到Cache中。查询query中可以设置多个fq进行Cache,但是值得注意的是多个fq都是以交集的结果返回。

filterCache存储了无序的lucenedocument id集合,该cache有3种用途:

1)filterCache存储了filterqueries(“fq”参数)得到的document id集合结果。Solr中的query参数有两种,即q和fq。如果fq存在,Solr是先查询fq(因为fq可以多个,所以多个fq查询是个取结果交集的过程),之后将fq结果和q结果取并。在这一过程中,filterCache就是key为单个fq(类型为Query),value为document id集合(类型为DocSet)的cache。对于fq为range query来说,filterCache表现出其有价值的一面。

2)filterCache还可用于facet查询(http://wiki.apache.org/solr/SolrFacetingOverview),facet查询中各facet的计数是通过对满足query条件的documentid集合(可涉及到filterCache)的处理得到的。因为统计各facet计数可能会涉及到所有的doc id,所以filterCache的大小需要能容下索引的文档数。

3)如果solfconfig.xml中配置了<useFilterForSortedQuery/>,那么如果查询有filter(此filter是一需要过滤的DocSet,而不是fq,我未见得它有什么用),则使用filterCache。

配置示例:

<!-- Internal cache used bySolrIndexSearcher for filters (DocSets),

unordered sets of *all* documents that match a query.

When a new searcher is opened, its caches may be prepopulated

or "autowarmed" using data from caches in the old searcher.

autowarmCount is the number of items to prepopulate.  For LRUCache,

the prepopulated items will be the most recently accessed items.

-->

<filterCache

class="solr.LRUCache"

size="16384"

initialSize="4096"

autowarmCount="4096"/>

对于是否使用filterCache及如何配置filterCache大小,需要根据应用特点、统计、效果、经验等各方面来评估。对于使用fq、facet的应用,对filterCache的调优是很有必要的。

3.2、documentCache

顾名思义,documentCache是用来保存<doc_id,document>对的。如果使用documentCache,就尽可能开大些,至少要大过<max_results> * <max_concurrent_queries>,否则因为cache的淘汰,一次请求期间还需要重新获取document一次。也要注意document中存储的字段的多少,避免大量的内存消耗。documentCache主要是对document结果的Cache,一般而言如果查询不是特别固定,命中率将不会很高。

配置示例:

<!-- documentCache caches Lucene Document objects (the stored fields for each document).
      -->
    <documentCache
      class="solr.LRUCache"
      size="16384"
      initialSize="16384"/>

3.3、queryResultCache

queryResultCache对Query的结果进行缓存,主要在SolrIndexSearcher类getDocListC()方法中被使用,主要缓存具有 QueryResultKey的结果集。也就是说具有相同QueryResultKey的查询都可以命中cache,所以我们看看 QueryResultKey的equals方法如何判断怎么才算相同QueryResultKey:

public boolean equals(Object o) {

if (o==this) return true;

if (!(o instanceof QueryResultKey)) return false;

QueryResultKey other = (QueryResultKey)o;

// fast check of the whole hash code... most hash tables will only use

// some of the bits, so if this is a hash collision, it‘s still likely

// that the full cached hash code will be different.

if (this.hc != other.hc) return false;

// check for the thing most likely to be different (and the fastestthings)

// first.

if (this.sfields.length != other.sfields.length) return false;//比较排序域长度

if (!this.query.equals(other.query)) return false;//比较query

if (!isEqual(this.filters, other.filters)) return false;//比较fq

for (int i=0; i<sfields.length; i++) {

SortField sf1 = this.sfields[i];

SortField sf2 = other.sfields[i];

if (!sf1.equals(sf2)) return false;//比较排序域

}

return true;

}

从上面的代码看出,如果要命中一个queryResultCache,需要满足query、filterquerysortFiled一致才行。因为查询参数是有start和rows的,所以某个QueryResultKey可能命中了cache,但start和rows却不在cache的document id set范围内。当然,document id set是越大命中的概率越大,但这也会很浪费内存,这就需要个参数:queryResultWindowSize来指定document id set的大小。

相比filterCache来说,queryResultCache内存使用上要更少一些,但它的效果如何就很难说。就索引数据来说,通常我们只是在索引上存储应用主键id,再从数据库等数据源获取其他需要的字段。这使得查询过程变成,首先通过solr得到document id set,再由Solr得到应用id集合,最后从外部数据源得到完成的查询结果。如果对查询结果正确性没有苛刻的要求,可以在Solr之外独立的缓存完整的查询结果(定时作废),这时queryResultCache就不是很有必要,否则可以考虑使用queryResultCache。当然,如果发现在queryResultCache生命周期内,query重合度很低,也不是很有必要开着它。

配置示例:

<!-- queryResultCache caches results of searches - ordered lists of

document ids (DocList) based on a query, a sort, and the range
         of documents requested.
      -->
    <queryResultCache
      class="solr.LRUCache"
      size="16384"
      initialSize="4096"
      autowarmCount="1024"/>

3.4 filterValueCache

fieldValueCache在SolrIdexSearcher的定义如下:

SolrCache<String,UnInvertedField> fieldValueCache; 其中key代表FieldName,value是一种数据结构UnInvertedField。 fieldValueCache在solr中只用于multivalued Field。一般用到它的就是facet操作。关于这个缓存需要注意的是,如果没有在solrconfig.xml中配置,那么它是默认存在的(初始大小10,最大10000,不会autowarm)会有内存溢出的隐患。

由于该cache的key为FieldName,而一般一个solrCore中的字段最多也不过几百。在这么多字段中,multivalued 字段会更少,会用到facet操作的则少之又少。所以在solrconfig.xml中的配置不必过大,大了也是浪费。

该缓存存储排序好的docIds,一般是topN。这个缓存占用内存会比filterCache小。因为它存储的是topN。但是如果QueryCommand中带有filter(DocSet类型),那么该缓存不会起作用。主要因为DocSet在执行hashcode和equals方法时比较耗时。

3.5 User/Generic Caches

Solr支持自定义Cache,只需要实现自定义的regenerator即可,下面是配置示例:

<!-- Example of a generic cache.  These caches may be accessed by name

through SolrIndexSearcher.getCache(),cacheLookup(), and cacheInsert().

The purpose is to enable easy caching of user/application level data.

The regenerator argument should be specified as an implementation

of solr.search.CacheRegenerator if autowarming is desired.

-->

<!--

<cache name="yourCacheNameHere"

class="solr.LRUCache"

size="4096"

initialSize="2048"

autowarmCount="4096"

regenerator="org.foo.bar.YourRegenerator"/>

-->

四、solr基于Cache 的优化

solr应用中为了提高查询速度有可以利用几种cache来优化查询速度,在日常使用中最为立竿见影,在这章节中基于之前做过的一个职位搜索的例子来介绍两个基于cache的优化,职位搜索截取集群中一个collection节点作为示例,数据量78万。

4.1查询结果缓存应用

将solrconfig.xml中所有的缓存配置都去掉,此时搜索条件为:jobsName:软件工程师 and educateBackground:本科 and workPlace:济南

查询结果如下图:

在solrconfig.xml中开启查询结果缓存配置

<queryResultCache class="solr.LRUCache"  size="512"  initialSize="512"autowarmCount="0"/>

<documentCache class="solr.LRUCache" size="512"  initialSize="512"   autowarmCount="0"/>

<queryResultMaxDocsCached>1024</queryResultMaxDocsCached>

从上面结果可以很明显看出查询走缓存的效果还是非常明显的,并且不用担心数据更新然后再次打开SolrIndexSearcher后缓存不一致的情况。当切换SolrIndexSearcher时,可以对新生成的SolrIndexSearcher做autowarm(预热)处理,会从旧的SolrIndexSearcher中取出原来缓存的项在新的SolrIndexSearcher中重新生成。

4.2基于filterCache查询优化

在常规的搜索查询中,查询经常会带一些基础查询条件,比如在职位搜索中经常会基于学历为本科,职位类型是全职等为前提条件的查询,这些前提条件变动小并且每次查询都要带上,这个时候filtercache就能很好的发挥出它的优势了。

为了验证filtercache,首先去除所有cache配置,重启搜索服务,在不使用任何cache的情况下不同搜索条件的耗时如下:


查询条件


查询耗时(ms)


jobDescrip: 文案编辑 and employeeSex:不限 and jobsType:全职and educateBackground:本科  and workPlace:深圳 and salary:面议


114


jobDescrip: 工程师 and employeeSex:不限 and jobsType:全职and educateBackground:本科  and workPlace:深圳 and salary:面议


147


jobDescrip: 高级软件开发工程师 and employeeSex:不限 and jobsType:全职and educateBackground:本科  and workPlace:深圳 and salary:面议


210

在去除其他cache配置条件下只添加filtercache配置:

<filterCacheclass="solr.FastLRUCache" size="512"

initialSize="512"

autowarmCount="0"/>

当查询条件为:jobDescrip: 软件工程师,其中filterquery为: employeeSex:不限 andjobsType:全职  and educateBackground:本科  and workPlace:深圳 and salary:面议。此时查询如下(重启后首次查询的耗时包含了初次搜索时基础数据的初始化):

接着进行二次搜索,搜索效果如下:

通过如下缓存命中监控图可以看出第二次搜索filtercache直接命中,命中率为50%。

此时在filterquery条件不变的情况下,只改变基础搜索词:jobDescrip:文案编辑

通过上图可以看出查询时间大大降低,此时再查看filtercache缓存命中率,总共命中两次,并且第二、三查询速度明显加快

但是在使用filterquery的时候需要注意query跟filterquery中不能有重复的字段,

例如:

Query=jobDescrip: 软件工程师and employeeSex:不限

Filterquery=employeeSex:不限 andjobsType:全职  and educateBackground:本科  and workPlace:深圳 and salary:面议

如上,条件中(employeeSex:不限)在query和filterquery中都会出现,这样的写法非但起不到查询优化的目的,而且还会增加查询的性能开销。

参考资料

Grainger T, Potter T, Seeley Y. Solr inaction[M]. Cherry Hill: Manning, 2014.

Ku? R. Apache Solr 4 Cookbook[M]. PacktPublishing Ltd, 2013.

https://wiki.apache.org/solr/SolrCaching#User.2FGeneric_Caches

http://www.solr.cc/blog/?p=239

原文地址:https://www.cnblogs.com/cuihongyu3503319/p/10747798.html

时间: 2024-10-03 16:46:44

solr中Cache综述的相关文章

solr入门之多线程操作solr中索引字段的解决

涉及的问题: 建索引时有一个字段是该词语出现的次数,这个字段是放在solr里的 而我用的是多线程来进行全量导入的,这里就涉及到了多线程问题 多个线程操作同一个变量时如何处理? 我是这样子做的 : 首先将变量本地话--分布式就放到大容器中,我这里仅仅使用了一个map来存 词和次数的关系映射 变量本地化后就是多线程的解决了--锁的设置-我仅仅是在操作时加了一个锁来解决这个问题 这样做后总体上应该能解决变量的问题了 最后还有一个线程顺序问题要解决下 当 最后一个提交索引时 获取的索引不一定是正确的索引

jfinal-ext shiro插件中cache无用

1.在项目中使用后,发现jfinal-ext shiro插件中cache配置无用 即,不管是否配置 <cache name="myRealm.authorizationCache" maxElementsInMemory="10000" overflowToDisk="true" eternal="true" timeToLiveSeconds="0" timeToIdleSeconds="

Oracle Sequence中Cache与NoCache的区别;合适使用

Oracle在创建序列(sequence)时有个参数你可以选择cache或者nocache,下面来讲一下两者的区别: 先来看下创建sequence的语句: create sequence SEQ_ID  minvalue 1  maxvalue 99999999  start with 1  increment by 1  cache n  / nocache  --其中n代表一个整数,默认值为20order; 如果指定CACHE值,Oracle就可以预先在内存里面放置一些Sequence,这样

指尖上的电商---(9).net开发Solr中的Facet功能

上一节中我们演示了在SolrAdmin中使用Facet功能来进行分组统计,这一节我们看看怎样使用.NET开发Solr中的Facet功能.在讲Facet功能的同时, 我们看下.Net中怎样使用Solr查询.使用的客户端工具是easysorl.net,大家可以去codeplex下载.这个工具很好用. 看如下图,下图就是我们要演示的功能   1.模糊查询 模糊查询就是搜索指定的汉字得到一个结果.下面的示例就是查询商品名称中包含白色的所有商品,最终得到的结果如下图 代码 public void Quer

在Lucene或Solr中实现高亮的策略

一:功能背景 最近要做个高亮的搜索需求,以前也搞过,所以没啥难度,只不过原来用的是Lucene,现在要换成Solr而已,在Lucene4.x的时候,散仙在以前的文章中也分析过如何在搜索的时候实现高亮,主要有三种方式,具体内容,请参考散仙以前的2篇文章: 第一:在Lucene4.3中实现高亮的方式  http://qindongliang.iteye.com/blog/1953409 第二:在Solr4.3中服务端高亮的方式  http://qindongliang.iteye.com/blog/

(solr系列:四)将mysql数据库中的数据导入到solr中

在前面的博文中,已完成了在tomcat中对solr的部署,为solr添加了一个自定义的core,并且引入了ik分词器. 那么该如何将本地的mysql的数据导入到solr中呢? 准备工作: 1.mysql数据源:myuser库中的user表(8条数据) /* Navicat MySQL Data Transfer Source Server         : localhost Source Server Version : 50521 Source Host           : local

[z]计算机架构中Cache的原理、设计及实现

前言 虽然CPU主频的提升会带动系统性能的改善,但系统性能的提高不仅仅取决于CPU,还与系统架构.指令结构.信息在各个部件之间的传送速度及存储部件的存取速度等因素有关,特别是与CPU/内存之间的存取速度有关. 若CPU工作速度较高,但内存存取速度相对较低,则造成CPU等待,降低处理速度,浪费CPU的能力. 如500MHz的PⅢ,一次指令执行时间为2ns,与其相配的内存(SDRAM)存取时间为10ns,比前者慢5倍,CPU和PC的性能怎么发挥出来? 如何减少CPU与内存之间的速度差异?有4种办法:

linux 内存中cache和buffer解析

cache是从磁盘读数据到内存中的缓存,减少读取磁盘的次数, 大家知道的硬盘读取速度过慢. buffer是准备从内存写到硬盘的缓存,缓存中的数据会进行合并 同时,避免频繁操作硬盘. linux 内存中cache和buffer解析

ASP.NET缓存中Cache过期的三种策略

原文:ASP.NET缓存中Cache过期的三种策略 我们在页面上添加三个按钮并双击按钮创建事件处理方法,三个按钮使用不同的过期策略添加ASP.NET缓存. <asp:Button ID="btn_InsertNoExpirationCache" runat="server" Text="插入永不过期缓存"      OnClick="btn_InsertNoExpirationCache_Click" />