生产环境Solr内存调优
生产的Solr内存调优(第1部分):
正确配置Apache Solr内存对于生产系统的稳定性和性能至关重要。很难在相互竞争的目标之间找到合适的平衡。还有许多因素,或明或暗,都需要考虑。本博客将讨论内存调优中的一些常见任务,并指导您完成整个过程,以帮助您理解如何为生产系统配置Solr内存。
为简单起见,本博客适用于运行在HDFS之上的Cloudera CDH5.11中的Solr。平台是Oracle JDK 8和64位Linux。这个博客分为两部分。
常见任务在内存中调优
在深入研究细节之前,让我们先看看内存调优中需要解决的一般问题。为了避免JVM内存不足(OOM)或沉重的GC开销,JVM堆大小必须匹配Solr的内存需求。这是内存调优的首要任务。JVM堆大小的设置很简单。它是在Solr配置中设置的(Cloudera Manager->Solr配置->堆大小)。另一方面,根据索引大小、工作负载和配置的不同,Solr的内存需求会有很大的不同。本博客将向您展示如何进行良好的评估,以便您能够匹配JVM堆大小和Solr内存的需要。
内存调优的另一个常见任务是找到内存使用和性能之间的最佳权衡。通常(但并非总是),Solr可以支配的内存越多,它的性能就越好。另一方面,给定的内存越多,硬件成本和JVM GC开销就越大。如果给定的内存大于某个点,那么性能改进就不能再证明这些缺点的合理性了。在这篇博客中,我们将向您展示一些如何在选择最优堆大小时找到最佳点的例子,以及给你一些其他的solr调优中的通常的最佳实践。
JVM GC调优也是一项常见的任务。Solr通常对GC友好,在大多数情况下不需要太多的微调。本博客将介绍几个可以提高Solr性能的最关键的GC调优旋钮。
Solr如何使用内存
在我们开始估算Solr内存需求之前,让我们快速了解一下Solr是如何使用内存的。Solr可以使用两种类型的内存,堆内存和直接内存(通常称为堆外内存)。直接内存用于缓存从文件系统读取的块,类似于Linux文件系统缓存。对于堆内存,下图显示了Solr内的各种主要使用者。
如您所见,堆内存的很大一部分被多个缓存使用。除了字段缓存之外,其他缓存都是每个core的。Solr core是一个索引片。一个Solr server通常有多个core。缓存使用的最大内存由solrconfig.xml中配置的单个缓存大小控制。
开始
在调优之前,请确保您的系统在索引大小和工作负载方面是平衡的。例如,使用可以在生产工作负载下创建均匀大小的collections和cores的分片路由器或集合别名。还要确保所有cores均匀分布在所有节点上。如果你想了解更多关于平衡设计的细节,请关注未来的Cloudera工程博客。
由于Solr内存需求取决于索引大小和工作负载,将索引和工作负载均匀地分布在所有Solr节点上大大简化了内存调优,避免了单个节点的瓶颈,最终有助于系统的稳定性和性能。该示例假设索引和工作负载均匀分布在所有Solr节点中。
作为将Solr部署到生产环境的第一步,使用一组“安全”配置参数启动Solr是个好主意。他们可能表现不佳。但是他们首先要确保Solr是稳定的,并为进一步的调优建立了坚实的基线。下面是一些需要启动的调优旋钮,其中一些将在博客第2部分的调优部分重新讨论。
JVM堆大小
如上所述,JVM堆大小应该匹配Solr堆要求,可以如下所示进行估计。
Solr heap requirement est. =
Filter cache size * (total doc in a core/ 8) * num of Cores +
Field value cache memory usage (if used) * num of Cores +
Field cache memory usage (if used) +
Misc memory usage (4G for busy system) +
Temporary workspace (4-6G for busy system)
如果使用cache autowarm,则将cache size替换为cache size + cache autowarm size。
Field cache是Solr中主要的内存使用者。减少内存占用的最好方法是通过使用docValues来避免它。下一节将提供更多细节。如果使用field cache,作为一个粗略的估计,如果工作负载很大,需要对单个值的字段进行facet和排序,并且索引大小很大(>50M文档或> 10G大小),那么使用8-12G的field cache内存。否则4-8G就可以了。Field value cache用于对多值字段进行faceting和sorting,遵循与字段缓存类似的指导原则。
JVM堆大小需要匹配Solr堆需求估计和一些缓冲区。理想情况下,30%的缓冲区空间适合生产系统。这为容纳零星的内存使用高峰提供了空间,例如后台合并或偶尔昂贵的查询,并允许JVM有效地执行GC。对于生产系统,一旦将堆的大小加起来,推荐的最小堆大小是16G。
直接内存
Solr使用直接内存缓存从磁盘读取的数据,主要是索引,以提高性能。直接内存不会导致任何JVM GC开销。直接内存大小可以使用CM (Cloudera Manager->Solr配置->直接内存)设置。根据经验,如果在模式中不使用docValues,建议生产系统的直接内存的最小大小为8G,如果使用docValues,则建议为12-16G。一个相关的配置是块缓存slab计数(Cloudera Manager->Solr配置->slab计数),它需要匹配直接内存大小。
Slab count = direct memory size * 0.7 / 128M
垃圾回收器
对于Oracle JDK 8, CMS和G1 GC都支持。根据经验,如果堆大小小于28G, CMS可以很好地工作。否则G1是一个更好的选择。如果您选择G1,在本博客的第2部分有更多关于G1配置的细节。您还可以在Oracle的G1调优指南中找到有用的指导。
同时,启用GC日志记录总是一个好主意。GC日志记录的开销是微不足道的,但它让我们更好地理解JVM如何在底层使用内存。这些信息在GC故障排除中是必不可少的。下面是GC日志记录设置的示例。
-XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintTenuringDistribution -XX:+PrintAdaptiveSizePolicy -XX:+PrintGCApplicationStoppedTime -XX:+PrintReferenceGC -Xloggc:/var/log/solr/solr_gc.log
在上面的例子中,GC日志存储在/var/log/solr/solr_gc.log。您可以将它指向任何其他路径。因为Solr在CDH中的Linux用户“Solr”下运行,所以只要确保Linux用户“Solr”对你所指向的路径有写权限就可以了。
Schema
如果您的工作负载在facet和某些字段中的排序方面比较繁重,那么对这些字段使用docValues。使用docValues Solr可以避免在堆上使用字段缓存和字段值缓存,这可以大大减少堆和JVM GC上的内存压力。另一方面,docValue字段可能导致更多的磁盘I/O,这将影响性能,并需要如上面的direct memory一节中提到的大量直接内存。
当使用文本字段进行分面和排序时,文本字段可能会导致大量内存使用。在本例中使用字符串字段而不是文本字段。
监控
监视是跟踪系统运行状况的主要手段之一。一般来说,有四个组件可以进行良好的监视:关键指标、仪表板、日志记录和警报。本博客将介绍内存相关的关键指标和仪表板。
关键的指标:
以下是需要监控的关键指标列表。指标分为两类:JVM指标和缓存指标。
Jvm_heap_used_mb告诉我们Solr在JVM中实际使用了多少堆。它应该在JVM堆大小部分的Solr内存需求估计上下波动。CM每分钟收集一份样本。因此,该指标可能不能反映所有内存使用峰值。正如前面提到的,确保堆中有足够的缓冲内存来容纳零星的内存使用高峰并减少GC开销。
缓存命中率是另一个需要监控的关键指标。它表示在总查询中有多少请求是通过缓存而不是命中磁盘上的索引得到满足(或部分满足)的。由于命中索引是昂贵的,更高的缓存命中率意味着更多的请求被缓存满足,这对性能有好处。关于缓存命中率的更多细节,请参见博客第2部分的缓存优化部分。
仪表盘:
监控这些关键指标的最简单方法是CM仪表板。您可以轻松地使用Cloudera Manager为每个指标使用类似于下面示例的查询构建一个指示板。
select jvm_heap_used_mb where serviceName=SOLR-1
下面是从上面的查询创建的指示板。
实时度量值和长期趋势都很重要。实时值显示Solr的执行情况。如果系统中发生了一些变化,比如工作负载、索引大小等,那么长期趋势是一个好迹象。
当你看长期趋势时,CM仪表板将执行下样本。例如,如果查看30天期间的JVM堆使用情况,仪表板会显示三个值:max、min和mean。对于JVM堆使用,除了平均值之外,还要监视最大值。
结论:
在将Solr引入生产环境时,内存调优是一个关键步骤。Solr中有许多调优旋钮,可以帮助您设置一个稳定且性能良好的系统。本博客解释了一般的内存调优技术,其中的一些调优旋钮,以及如何使用一组专注于稳定性的配置启动您的第一次生产部署。该博客还描述了Solr内存监控,这对于确保Solr部署的稳定性和长期性能至关重要。
现在我们有一个稳定的Solr系统运行。本博客的第2部分将更深入地向您展示如何调优内存以从Solr获得更多,以及如何进行GC调优。第2部分还介绍了一些最佳实践。
生产的Solr内存调优(第2部分)
在本博客的第1部分中,我们讨论了与Solr生产部署相关的内存调优和基线设置方面的一些常见挑战。在第2部分中,你将学习内存调优、GC调优和一些最佳实践。
内存调优
我们假设您已经阅读了本博客的第1部分,并且已经有一个稳定的Solr部署正在运行。下一步是内存调优,以从Solr中获得更多。在更改任何配置之前,请注意使用一些调优旋钮可能会对系统造成意想不到的后果,比如系统崩溃。因此,强烈建议在与生产系统类似的沙盒系统中尝试这些参数。重要注意:如果您的沙箱系统不能与生产系统相比较,那么您可能会为错误的工作负载进行调优。
在调优过程中,改变缓存大小将改变Solr内存需求,如本博客的第1部分所示。请相应地调整JVM堆大小,以匹配Solr内存需求。
缓存大小
缓存可以大大提高性能。它基于对时间局域性的观察,即相同的查询很可能在短时间内再次命中Solr。为了利用这一观察结果,查询的结果将缓存在内存中。当相同的查询到达时,它可以满足从内存缓存的结果,而不需要在磁盘上查找索引。
缓存中满足的查询越多,性能就越好。另一方面,缓存可能会使用大量内存,增加硬件成本和JVM GC开销或额外的GC周期。不幸的是,有时很难对系统进行调优,以同时满足高性能和内存占用少的要求。更糟糕的是,有时工作负载是如此的多样化,以至于在很短的时间内几乎没有重复的查询。在这种情况下,即使是一个大缓存也不能提高性能。调优的目标是找到性能和内存占用之间的最佳平衡点。
衡量缓存有效性的一个关键指标是缓存命中率,即缓存满足查询的百分比。正如博客第1部分的监控一节中提到的,您可以使用Cloudera Manager监控缓存命中率。理想的命中率是90%以上的生产系统。缓存命中率主要取决于缓存大小和工作负载模式。虽然需要详细分析工作负载以找出最佳缓存设置,但下图显示了一些可以极大地提高缓存有效性的简单步骤。
首先,将生产工作负载应用于系统并监视缓存命中率。在本博客的第1部分中,您可以在监控一节中找到如何监控缓存命中率。
如果缓存命中率很高(>85%),那就很好。调优的目标是最小化内存需求,同时保持如上图所示的高命中率。您可以重复减少缓存大小和监视器命中率,直到命中率太低。
如果缓存命中率较低,则根据您的硬件可用性将缓存大小增加到允许的最大大小。例如,假设服务器上还有16G额外的内存可以分配给Solr,您正在调优过滤器缓存。如上所示,每个过滤器缓存条目最多可以使用12.5万字节的内存。然后,您可以将缓存大小增加大约16G*0.7/12.5M=900,并将JVM堆大小增加16G。如果缓存命中率变得很高,继续上一步,直到找到最佳缓存大小。
如果在将缓存大小增加到允许的最大值后,缓存命中率仍然很低,这通常意味着工作负载中的查询太过多样,工作负载不能利用缓存。在这种情况下,高命中率不再是一个合理的目标。主要的调优目标是最小化内存需求,如上图所示。
使用Solr缓存的逻辑与普通查询不同,例如faceting。不要完全禁用缓存,即使缓存在性能上几乎没有帮助。
AutoSoftCommit
AutoSoftCommit配置参数是索引项对查询可见之前的最大延迟. 它会影响缓存的效率。当软提交发生时,Solr会清除缓存。软提交后的查询在缓存满足之前就会到达磁盘。
通常有一个关于允许的最大延迟的业务要求。尝试设置最大AutoSoftCommit范围,但通常不超过30秒。
Cache Autowarm
软提交后优化空缓存的另一种方法是缓存自动升温。Solr将通过预先填充一些缓存项来预热缓存。通过这种方式,一些查询可以通过缓存立即得到满足,而不会碰到磁盘,因此提高了性能。
另一方面,缓存自动加热会显著增加内存需求。这是因为旧缓存和新缓存可以在Solr堆中共存,这使缓存内存需求加倍,这意味着更多的硬件和JVM GC开销。
您可以使用与缓存大小调优 类似的过程来找到最佳的缓存自动加热配置,参见上面的内容。
GC调优
正如博客第1部分内存调优一节中的常见任务所提到的,内存调优中最重要的事情是确保JVM堆大小与Solr堆需求匹配。这也是确保GC以健康的方式工作的最重要的事情。此外,还有几个调优旋钮可以提供帮助。
年轻代空间大小(CMS)
在JVM中,当使用CMS GC时,堆被分为两个区域:年轻空间和老Gen.这是基于一个观察结果,即在堆上分配的大多数对象很快就会释放。年轻空间为这种短命的对象进行了优化,并且在默认情况下在那里分配对象。如果一些对象被证明是长期存在的,它们将在以后被移到老年代。
年轻代空间GC(又称小GC)仅为年轻空间中的死对象回收内存。理想情况下,它每10 - 30秒发生一次。如果次要GC发生得太频繁,则意味着年轻的空间大小太小。下面的示例展示了将年轻空间大小增加到4G的JVM选项。-XX:NewSize=4G -XX:MaxNewSize=4G
您可以使用CM jvm_gc_rate度量来监视年轻空间GC事件频率,如博客第1部分的监视部分所述。严格地说,jvm_gc_rate指标包括年轻空间GC和并发周期的GC事件。在实践中,由于并发周期的比率通常比年轻空间GC的比率要低得多,因此您可以使用这个指标来获得较好的估计。
如果您真的喜欢深入研究,您可以从JVM GC日志中找出每种类型GC事件的频率。建议将GC日志用于生产部署,您可以在博客第1部分的垃圾收集器一节中找到指导。
增加年轻空间大小会减少较小的GC,但它也会减少老年代中的可用内存,这可能会增加昂贵的并发周期的速率。因此,你需要在两者之间找到最好的平衡。在实践中,年轻空间大小不应超过堆总大小的40%。
G1垃圾收集器:
大堆内存(>28G)可能并不总是提高性能的好方法。一个非常大的堆中的大部分空间通常被大型缓存占用。正如前面所解释的,从这些大型缓存获得的性能高度依赖于工作负载模式,可能无法抵消额外的内存成本和GC开销。但是,如果您确信您的系统可以从大堆内存中获益,那么这里有一些建议。
对于较大的堆,建议使用G1。以下是建议启动的配置。
-XX:+UseG1GC -XX:MaxGCPauseMillis=500
-XX:+UnlockExperimentalVMOptions
-XX:G1MaxNewSizePercent=30
-XX:G1NewSizePercent=5
-XX:G1HeapRegionSize=32M
-XX:InitiatingHeapOccupancyPercent=70
MaxGCPauseMillis设置最大GC暂停时间目标。G1GC会尝试满足这个目标,但并不总是有保证。这是G1GC中最常用的调优旋钮。您可以减少目标,以指示G1GC减少最大GC暂停时间。另一方面,一个积极的目标可能导致G1GC过度优化,在某些情况下最终会下降,这可能导致更长的GC暂停时间。更多的细节可以在Oracle的G1调优指南中寻找。
最佳实践:
Pre-logging
默认情况下,Solr会在查询完成后记录查询信息和度量。如果Solr在查询执行期间耗尽内存,那么导致问题的查询永远不会结束,因此永远不会记录日志。在这种情况下,无法知道是哪个查询导致了这个问题。要找出这样的查询,您可以启用预日志记录。
log4j.logger.org.apache.solr.core.SolrCore.Request=DEBUG
Faceting and Sorting Queries:
Faceting and Sorting查询会使用大量的内存。尝试使用docValues,并避免在faceting和sorting查询中使用text字段。在博客第1部分的schema部分有更多关于docValues的描述。
查询中的now参数:
对于时间序列应用程序,使用以下模式进行查询是很常见的
q=*:*&fq=[NOW-3DAYS TO NOW]
然而,从记忆的角度来看,这并不是一个好的做法。在底层,Solr将“NOW”转换为一个特定的时间戳,即查询点击Solr的时间。因此,具有相同字段查询fq=[NOW- 3days TO NOW]的两个连续查询被认为是不同的查询,一旦’ NOW '被两个不同的时间戳替换。结果,这两个查询都会碰到磁盘,不能利用缓存。
在大多数用例中,最后一分钟丢失的数据是可以接受的。因此,如果您的业务逻辑允许,请尝试以以下方式查询。
q=*:*&fq=[NOW/MIN-3DAYS TO NOW/MIN]
结论:
本博客描述了一些常用的调优旋钮,向您展示如何调优Solr以获得更好的性能,如何调优JVM GC和一些最佳实践。我们假设您已经阅读了本博客的第1部分,该部分解释了一般内存调优技术,以及如何开始您的第一个可靠的生产部署和内存监视。
一旦Solr在生产环境中运行,就需要定期执行内存调优。这是因为最佳的Solr内存配置高度依赖于工作负载和索引大小。在生产系统中,工作负载和索引很少不随时间变化。定期的调整可以确保Solr在良好的状态下运行。
本文翻译自Cloudera的技术博客,原文链接如下
solr内存调优第一部分
solr内存调优第二部分