Solr配置:缓存预热管理指南
Solr的缓存系统为提高查询性能提供了重要的优化手段。缓存可以存储文档、查询中使用的过滤器以及先前查询的结果。
缓存在提交(commit)后会被清空,通常需要重新填充才能再次发挥作用。为了解决这个问题,缓存可以在新搜索器被认为开放之前进行”预热”,通过自动使用旧缓存中的值填充新缓存。
缓存管理对于成功的Solr实现至关重要,因此应该注意的是,随着应用程序的增长,缓存将需要进行微调。
solrconfig.xml中的<query>配置
本节中的设置影响Solr处理和响应查询的方式。
这些设置都在solrconfig.xml中的<query>元素的子元素中配置。
1 | <config> |
缓存机制详解
Solr缓存与特定的Index Searcher实例相关联,这是索引的特定视图,在该搜索器的生命周期内不会更改。只要该Index Searcher正在使用,其缓存中的任何项目都将保持有效并可供重用。
默认情况下,缓存的Solr对象不会在时间间隔后过期;相反,它们在Index Searcher的生命周期内保持有效。可以通过使用maxIdleTime选项启用基于空闲时间的过期。
当新搜索器打开时,当前搜索器继续为请求提供服务,而新搜索器自动预热其缓存。新搜索器使用当前搜索器的缓存来预填充自己的缓存。当新搜索器准备就绪时,它被注册为当前搜索器并开始处理所有新的搜索请求。一旦旧搜索器完成为其所有请求提供服务,就会关闭它。
缓存实现
Solr附带了一个默认的SolrCache实现,用于不同类型的缓存。
CaffeineCache是由Caffeine缓存库支持的实现。默认情况下,它使用Window TinyLFU(W-TinyLFU)逐出策略,该策略允许基于使用频率和最近性在O(1)时间内逐出,占用空间小。通常,这种缓存相比传统缓存提供更低的内存占用、更高的命中率和更好的多线程性能。
CaffeineCache使用支持整数和百分比的自动预热计数,这些计数相对于预热时缓存的当前大小进行评估。
Solr管理UI中的插件/统计屏幕将显示有关所有活动缓存性能的信息。此信息可以帮助您为特定应用程序适当调整各种缓存的大小。当搜索器终止时,其缓存使用情况的摘要也会写入日志。
缓存参数
每个缓存都有设置来定义其初始大小(initialSize)、最大大小(size)和预热期间使用的项目数(autowarmCount)。对于autowarmCount,这也可以表示为百分比而不是绝对值。
maxIdleTime属性控制长时间未使用的条目的自动逐出。此属性以秒为单位表示,默认值0表示由于超出空闲时间而不会自动逐出任何条目。此属性的较小值将导致旧条目快速逐出,这将减少缓存内存使用,但可能会导致相同条目的重复逐出-查找-未命中-插入循环而出现抖动。较大的值将导致条目停留更长时间,等待被重用,代价是增加内存使用。根据查询量和模式,合理的值可能在60-3600之间。
maxRamMB属性限制缓存可能消耗的最大内存量。当同时指定size和maxRamMB限制时,maxRamMB限制将优先,size限制将被忽略。
async属性确定缓存是存储直接结果(async=false,禁用)还是存储对计算的间接引用(async=true,默认启用)。如果您的查询包括子文档或连接查询,则必须启用异步缓存才能正常运行。禁用异步选项可能会以增加CPU为代价,每个缓存条目使用稍少的内存。异步缓存在许多并发查询请求尚未缓存的相同结果集时提供最重要的改进,作为更大缓存大小或增加自动预热计数的替代方案。但是,异步缓存不会阻止时间限制查询的数据竞争,因为这些查询预期提供部分结果。
可以使用值为false的参数enabled禁用所有缓存。
以下描述了每个缓存的详细信息。
过滤缓存(Filter Cache)
此缓存保存已解析的查询及其匹配的所有文档的无序集合。除非这样的集合很小,否则集合实现是位集。
Solr使用filterCache的最典型方式是缓存每个fq搜索参数的结果,不过还有其他一些用例。使用相同参数过滤查询的后续查询会导致缓存命中并快速返回结果。
提示: 可以使用cache本地参数为特定查询的fq禁用此缓存的使用。
Solr使用此缓存的另一个功能是默认Lucene查询解析器中的filter(...)语法。
当配置参数facet.method设置为fc时,Solr也使用此缓存进行分面。
1 | <filterCache class="solr.CaffeineCache" |
缓存支持maxRamMB参数,该参数限制此缓存使用的最大堆量。CaffeineCache仅支持按堆使用量或大小逐出,但不能同时支持两者。因此,如果指定了maxRamMB,则忽略size参数。
1 | <filterCache class="solr.CaffeineCache" |
过滤缓存是启用async计算的良好候选者。
1 | <filterCache class="solr.CaffeineCache" |
查询结果缓存(Query Result Cache)
queryResultCache保存先前搜索的结果:基于查询、排序和请求的文档范围的有序文档ID列表(DocList)。
queryResultCache有一个可选设置来限制使用的最大RAM量(maxRamMB)。这让您可以指定此缓存内容使用的最大堆大小(以兆字节为单位)。当缓存增长超过此大小时,最旧访问的查询将被逐出,直到缓存的堆使用量降至指定限制以下。如果除了maxRamMB之外还指定了size,则仅遵守堆使用量限制。
1 | <queryResultCache class="solr.CaffeineCache" |
文档缓存(Document Cache)
documentCache保存Lucene文档对象(每个文档的存储字段)。由于Lucene内部文档ID是临时的,所以此缓存不会自动预热。
documentCache的大小应始终大于max_results乘以max_concurrent_queries,以确保Solr在请求期间不需要重新获取文档。您在文档中存储的字段越多,此缓存的内存使用量就越高。
1 | <documentCache class="solr.CaffeineCache" |
注意: 不要对documentCache使用maxRamMB设置。缓存文档所需的内存量不会被正确计算,这可能导致缓存使用比预期多得多的内存。
用户自定义缓存
您还可以为自己的应用程序代码定义命名缓存以供使用。您可以通过调用SolrIndexSearcher方法getCache()、cacheLookup()和cacheInsert()按名称定位和使用缓存对象。
1 | <cache name="myUserCache" class="solr.CaffeineCache" |
如果您希望缓存自动预热,请包含一个regenerator属性,其中包含实现solr.search.CacheRegenerator的类的完全限定名称。您也可以使用NoOpRegenerator,它只是用旧项目重新填充缓存。使用regenerator参数将其定义为regenerator="solr.NoOpRegenerator"。
监控缓存大小和使用情况
缓存统计部分描述了每个缓存可用的指标。可以在插件/统计屏幕中或使用指标API访问这些指标。
评估缓存时要检查的最重要指标是大小和命中率。
大小表示缓存中有多少项目。某些缓存支持以MB为单位设置最大缓存大小。
命中率是缓存提供查询服务的百分比,显示为0和1之间的数字。较高的值表示缓存经常被使用,而较低的值表明缓存对查询的帮助不大。理想情况下,此数字应尽可能接近1。
如果您发现命中率很低但缓存大小设置很高,则可以通过减少缓存大小来进行优化-当不使用这些对象时,没有必要将它们保留在内存中。
另一个有用的指标是缓存逐出,它测量从缓存中移除的对象。高逐出率可能表明您的缓存太小,增加它可能会显示更高的命中率。或者,如果您的命中率很高但逐出率很低,您的缓存可能太大,您可能会从减少大小中受益。
低命中率并不总是特定缓存问题的征象。如果您的查询不经常重复,则会预期低命中率,因为缓存对象需要重用的可能性较小。在这些情况下,较小的缓存大小可能是系统的理想选择。
查询大小调整和预热
有几个元素可用于控制查询的大小以及如何预热缓存。
<maxBooleanClauses>元素
设置解析布尔查询字符串时允许的最大子句数。
此限制仅影响用户作为查询字符串的一部分指定的布尔查询,并提供对用户指定的布尔查询复杂程度的每个集合控制。指定超过此数量子句的查询字符串将导致错误。
如果此每个集合限制大于在solr.xml中指定的全局maxBooleanClauses限制,则它将没有效果,因为该设置也限制用户指定的布尔查询的大小。
在默认配置中,如果指定了solr.max.booleanClauses系统属性,则此属性使用该值。这与默认solr.xml中全局maxBooleanClauses设置中使用的系统属性相同,使Solr管理员可以轻松增加两个值(在所有集合中),而无需搜索和更新每个集合中的solrconfig.xml文件。
1 | <maxBooleanClauses>${solr.max.booleanClauses:1024}</maxBooleanClauses> |
<minPrefixQueryTermLength>元素
基于前缀的查询消耗的资源与以指定前缀开头的索引中的术语数成比例。特别短的前缀(例如只有一到两个字符的前缀)往往匹配索引的很大比例,是资源争用和不稳定的常见原因。此设置为查询建立最小前缀长度,为管理员提供一种阻止可能导致稳定性问题的查询的方法。不符合此最小前缀长度的查询会触发错误。可以通过提供具有不同值的minPrefixQueryTermLength“本地参数”,按查询覆盖设置。
该设置旨在管理所有基于前缀的查询(例如val_s:a*、{!prefix f=val_s}a、{!complexphrase}val_s:"a*")。
在默认配置集中,最小前缀设置为’-1’(具有”无限制”语义的标志值),或者solr.query.minPrefixLength系统属性的值(如果指定)。
<enableLazyFieldLoading>元素
当此参数设置为true时,未直接请求的字段将仅在需要时加载。
如果最常见的查询只需要字段的一小部分,特别是如果不经常访问的字段很大,这可以提高性能。
1 | <enableLazyFieldLoading>true</enableLazyFieldLoading> |
<useFilterForSortedQuery>元素
此设置仅影响请求的排序不包括”score”的查询(或者score无关紧要的查询-例如,没有请求文档,查询输出常数分数)。在这种情况下,将此元素配置为true会导致查询filterCache以获取与主查询匹配的过滤器。如果使用不同的排序选项或通过offset或cursorMark进行不同的分页(再次,前提是”score”未包含或与请求的排序无关)重复发出相同的搜索,这可能很有用。如果启用此选项,请确保filterCache有足够的容量来支持预期的使用模式。
1 | <useFilterForSortedQuery>true</useFilterForSortedQuery> |
<queryResultWindowSize>元素
与queryResultCache一起使用,这将缓存请求文档ID数量的超集。
例如,如果查询请求文档10到19,并且queryWindowSize是50,则将缓存文档0到49。
1 | <queryResultWindowSize>20</queryResultWindowSize> |
<queryResultMaxDocsCached>元素
此参数设置要为queryResultCache中任何条目缓存的最大文档数。
1 | <queryResultMaxDocsCached>200</queryResultMaxDocsCached> |
<useColdSearcher>元素
此设置控制没有当前注册搜索器的搜索请求应该等待新搜索器预热(false)还是立即进行(true)。设置为false时,请求将阻塞,直到搜索器预热其缓存。
1 | <useColdSearcher>false</useColdSearcher> |
<maxWarmingSearchers>元素
此参数设置在任何给定时间可能在后台预热的搜索器的最大数量。超过此限制将引发错误。
对于只读从节点,值2是合理的。主节点应该设置得稍高一些。
1 | <maxWarmingSearchers>2</maxWarmingSearchers> |
查询相关监听器
如缓存部分所述,新搜索器被缓存。可以使用监听器触发器来执行与查询相关的任务。最常见的用途是定义查询来进一步”预热”正在启动的搜索器。这种方法的一个好处是字段缓存被预填充以进行更快的排序。
良好的查询选择是此类监听器的关键。最好选择您最常见和/或最重的查询,不仅包括使用的关键词,还包括任何其他参数,如排序或过滤请求。
有两种类型的事件可以触发监听器。
- 当正在准备新搜索器但没有当前注册的搜索器来处理请求或获取自动预热数据时(即,在Solr启动时),会发生
firstSearcher事件。 - 每当正在准备新搜索器时(如提交后)且有当前搜索器处理请求时,就会触发
newSearcher事件。
下面的(注释掉的)示例可以在Solr附带的sample_techproducts_configs配置集的solrconfig.xml文件中找到,并演示使用solr.QuerySenderListener类来预热一组显式查询:
1 | <listener event="newSearcher" class="solr.QuerySenderListener"> |
重要提示:
上述代码来自示例solrconfig.xml。
一个关键的最佳实践是在将应用程序投入生产之前修改这些默认值,但请注意:虽然”newSearcher”部分的示例查询被注释掉了,但”firstSearcher”事件的示例查询没有被注释掉。
如果查询字符串”static firstSearcher warming in solrconfig.xml”与您的搜索应用程序无关,那么使用它来自动预热您的搜索器就没有意义。
使用配置API管理预热查询
可以使用配置API使用如下命令管理预热查询。
可以提供具有不同name属性的多个集合,任何更改都将立即生效,无需重新启动。
添加预热查询集
要添加一组预热查询,请使用add-listener命令。(您可能希望使用单独的命令和name也提供firstSearcher查询。)
1 | { |
更新查询集
要更新集合,请使用update-listener命令。注意整个集合将被替换。
1 | { |
如果在solrconfig.xml中定义了预热查询,如果将name属性添加到每个<listener>元素,则可以使用配置API覆盖它们。
删除查询集
要删除一组查询,请使用delete-listener:
1 | { |
注意,对于最初在solrconfig.xml中定义的预热查询集,使用delete-listener命令将恢复到solrconfig.xml集而不是删除预热查询。