本文将尝试回答以下问题:
- 为什么Solr的表现如此糟糕?
- 为什么Solr需要很长时间才能启动?
- 为什么SolrCloud像我的服务器一样在没事的时候出现故障?
这是试图仅提供基本信息。 为了更好地理解所涉及的问题,请阅读包含的链接,查找其他资源,并通过Solr支持资源提出深思熟虑的问题。
一般信息
在版本6.4.0和6.4.1中有一个性能缺陷使得* everything *慢。问题在6.4.2中解决。它由SOLR-10130进行描述。这是高度特定的版本,所以如果您没有运行某个受影响的版本,请不要担心。
1、RAM是Solr性能的主要驱动因素。 Solr需要足够的内存用于两个单独的事情:一个是Java堆,另一个是用于操作系统磁盘高速缓存的“空闲”内存。
2、另一个潜在的问题来源是非常高的查询率。增加内存有时可以让Solr处理更高的速度。如果需要更多的查询可伸缩性,最终这还不够,并且您需要在多台机器(最好是单独的物理硬件)上添加多个索引副本以进一步扩展。对于独立的Solr实例,或将SolrCloud与不支持云的客户端结合使用时,多个副本可能需要负载平衡器。
强烈建议Solr在64位Java上运行。 64位Java需要64位操作系统,而64位操作系统需要64位CPU。 32位软件或硬件没有问题,但是32位Java仅限于2GB堆,这可能会导致人为限制,而堆不会存在。 Java堆将在本页后面的章节中讨论。
SolrCloud
无论节点数量或可用资源如何,当Collection数量达到数百个时,SolrCloud开始出现稳定性问题。 数千个Collection,对集群的任何小问题或更改都可能导致稳定性死亡螺旋,而这种螺旋可能无法在几十分钟内恢复。尽量保持Collection数量尽可能低。这些问题是由于SolrCloud更新ZooKeeper中的群集状态以响应群集更改。正在努力尝试改善这种情况。这个问题出现在Solr 4.x中,状态保存在一个单独的“clusterstate.json”文件中。后续Solr版本(5x及以上版本)默认将每个集合的数据作为每个集合的znode(例如/collections/my_collection/state.json)存储在单个“state.json”中。如果您从Solr 4x安装开始,则MIGRATESTATE命令将更改为更新,更具可伸缩性的状态。也就是说,Zookeeper的负载肯定随着收集(和副本)数量的增加而增加。最近的Solr版本在数千个副本中表现良好。
由于SolrCloud严重依赖ZooKeeper,因此如果您的基础性能问题导致操作时间超过zkClientTimeout,则它可能非常不稳定。增加超时可能会有所帮助,但解决潜在的性能问题会产生更好的结果。默认超时(内部为15秒,在最近的示例配置中为30秒)很长,对于经过良好调整的SolrCloud安装应该足够了。
ZooKeeper的设计假定它对数据库的访问速度非常快。如果ZooKeeper数据库存储在保存Solr数据的相同磁盘上,则Solr的任何性能问题都会延迟ZooKeeper访问其自己的数据库的权限。这可能会导致性能死亡螺旋,每个ZK超时会导致恢复操作,从而导致进一步的超时。
ZooKeeper在Java堆内存中保存其数据库,因此磁盘读取性能不像磁盘写入性能那么重要。如果操作系统磁盘缓存太小而不能满足Solr的需求,并且ZK数据库与Solr数据位于同一磁盘上,则Solr的大量磁盘访问会干扰ZK写入。使用非常快速的ZK磁盘(特别是SSD)可以获得良好的性能。强烈建议为Solr和ZK数据使用单独的物理磁盘。对所有ZK节点(至少需要三个冗余节点)使用专用机器甚至更好,但不是严格要求。
内存
对于索引更新,Solr依靠快速批量读取和写入。对于搜索,快速随机读取是必不可少的。满足这些要求的最佳方式是确保提供大型磁盘缓存。访问Uwe的博客条目,了解一些好的Lucene / Solr特定信息。您还可以利用固态硬盘来加速Solr,但请注意,这不是完全取代OS磁盘高速缓存。有关更多详细信息,请参阅本文档后面的SSD部分。
简而言之,您希望在操作系统磁盘缓存中提供足够的内存,以便索引中重要的(经常访问的)部分可以放入缓存中。假设您的Solr索引大小为8GB。如果您的操作系统,Solr的Java堆以及所有其他正在运行的程序需要4GB内存,那么该服务器的理想内存大小至少为12GB。您可能可以使其工作在8GB总内存(为磁盘缓存留下4GB),但这也可能不够。真正重要的是确保操作系统磁盘高速缓存的高速缓存命中率不会达到完美。
如果您需要尽可能降低您的整体查询延迟,那么最好的办法就是拥有足够的系统内存来完全缓存整个索引。如果您不需要最小的延迟,则可能不需要缓存整个索引。
确切的最低要求是高度可变的,取决于您的模式,索引内容和查询等内容。如果您的索引具有大量存储字段,那么这些要求将位于比例较小的一端。如果您的储存数据非常少,则您希望处于比例较高的位置。存储数据的大小并不会影响搜索速度,尽管可能会影响一旦确定所需文档后检索搜索结果所需的时间。
在这里需要注意的是,没有快速公式可用于确定获得良好性能所需的最小内存量。建立一个完整的系统可能是你唯一可以知道的方法。有时候一个小规模的原型可以揭示有用的信息。请参阅本文以获取关于此主题的讨论。
有关优化操作的特别说明
优化索引会导致大量的磁盘I / O。如果您没有足够的操作系统磁盘高速缓存来有效地缓存索引,优化操作可能会对普通的Solr操作造成很大的破坏。如果优化是经常发生的事情,则可能需要有足够的RAM来缓存至少100%的索引内容。如果您有足够的RAM同时缓存索引的原始版本和优化版本,则优化期间的性能将最好。如果在主服务器上发生大的合并或优化,此说明也适用于进行复制的从属服务器。
Java Heap
Java堆是Java程序(如Solr)为实际运行所需的内存。
如果您在日志中看到OutOfMemoryError(OOME)异常,则可能会在此处结束。如果你看到这一点,你的Solr安装需要更多的资源而不是允许访问的资源。处理OOME只有两种选择。一种是增加资源用尽的规模,另一种是减少Solr需要的资源量。它通常是触发OOME的堆内存,但它可能是一些其他资源,如允许运行的线程/进程数。有人可能会说有第三种选择:修复程序中的错误,使其需要这么多的资源。这不是第三种选择 - 它属于第二种选择。
Solr中的某些配置和条件将需要大量堆内存。以下列表不完整,但没有特定顺序,其中包括:
- 一个大的索引。
- 频繁更新。
- 超大文件。
- 广泛使用faceting。
- 使用很多不同的排序参数。
- 非常大的Solr缓存
- 一个大的RAMBufferSizeMB。
- 使用Lucene的RAMDirectoryFactory。
我需要多少堆空间?
简短版本:
这是没有通用答案的那些问题之一。你需要一个足够大的堆,这样你就不会有OOME异常和持续垃圾回收问题,但又足够小,不会浪费内存或运行巨大的垃圾回收暂停。
长版:你必须试验。
Java开发工具包(JDK)附带两个GUI工具(jconsole和jvisulavm),您可以连接到Solr的运行实例并查看随着时间的推移使用了多少堆。对于较长期的JVM堆,内存空间和垃圾回收监视,可以使用SPM等工具。 JVM内存池监视上的文章显示了内存池报告中要查找的内容以避免OOME。
这个jconsole示例中的图表显示了典型的锯齿模式 - 内存使用率攀升至峰值,然后垃圾收集释放一些内存。确定有多少集合太多取决于您的查询/更新量。一个可能的经验法则:查看Solr每秒钟查询的次数。如果每分钟垃圾收集数量超过该值,那么堆可能太小。它也可能非常好......调整好的垃圾收集可能会频繁地进行大量非常快速的收集。
如果让Solr服务器以较高的查询和更新负载运行,锯齿图案中的低点将表示所需的绝对最小内存。试着设置你的最大堆在这个值的125%和150%之间,然后重复监视,看看锯齿模式中的低点是否明显高于以前,或者垃圾收集频繁发生。如果是,则用更高的最大堆重复测试。
额外的经验法则:更多的堆通常更好,但是如果它太大,花费在垃圾收集上的时间会变得极端。这个问题将在下面讨论。另外,如上所述,删除OS磁盘高速缓存的大小可能会产生更多问题。
减少堆需求
调整垃圾收集参数不会减少Solr需要的内存量!它可能会使回收内存效率更高,但它完全不会降低程序所需的内存量。它可能会使锯齿形记忆图中的高点更低,但它不会对低点做任何事情。如果您遇到OutOfMemory错误,对GC调整的更改将无济于事。更高效的垃圾收集可能会导致程序花费更长的时间,直到出现OOME,但它无法阻止它。
以下列出了一个不完整的列表,它们以特定的顺序列出了如何减少堆需求,基于上面列出的需要大量堆的东西:
获取大量索引并使其分发 - 将索引分解为多个分片。
一个非常简单的方法就是切换到SolrCloud。您可能需要新建索引,但SolrCloud将为您处理所有分片。
这实际上并没有减少大型索引的整体内存要求(实际上它可能会稍微增加),但是会将其分布到多个服务器上,因此每个服务器的内存要求都较低。为了冗余,不同服务器上应该有多个副本。
如果查询率非常低,则将多个分片放在一台服务器上的性能会很好。随着查询率的增加,每个服务器只有一个分片副本变得非常重要。不要存储所有的字段,尤其是非常大的字段。
相反,让您的应用程序检索原始数据源中的详细数据,而不是Solr。
请注意,这样做意味着您无法使用原子更新。您还可以在用于排序/构面和重新索引的字段上启用docValues。
减少不同排序参数的数量。就像facets一样,docValues可以对排序的性能和内存使用率产生积极的影响。
减少Solr高速缓存的大小。
减少RAMBufferSizeMB。最近Solr版本的默认值是100。
如果你有很多内核,这个值就特别重要,因为每个内核都会使用一个缓冲区。不要使用RAMDirectoryFactory - 而是使用默认值并安装足够的系统RAM,以便操作系统可以缓存整个索引,如上所述。
GC暂停问题
当你有一个大堆(大于2GB)时,垃圾收集暂停可能是一个主要问题。 这通常是由偶尔需要的“必须停止世界”的完整垃圾回收引起的 - 暂停所有程序执行以清理内存。 有两个主要的解决方案:一个是使用像Zing这样的带有价格标签的商业低暂停JVM。 另一个是调整你已经拥有的免费JVM。 GC调整是一种艺术形式,适合一个人的东西可能不适合你。
使用具有调整参数的ConcurrentMarkSweep(CMS)收集器对于Solr来说是一个非常好的选择,但对于最新的Java 7版本(撰写本文时为7u72),G1看起来是更好的选择,如果-XX:+ParallelRefProcEnabled选项。 来自专注于GC的Oracle工程师的信息表明,最新的Java 8将显着提高Java 7的G1性能,但尚未得到证实。 这里有一些想法,希望你会发现有帮助:
G1调整技巧
Oracle Java 6 GC调优指南
使用CMS手动调整各种堆代的大小非常重要。 G1收集器在运行时会自动调整代码的大小,并且强制大小通常会导致性能下降。
如果你的最大堆只是有点太小,你最终可能会有一个稍微不同的垃圾收集问题。 这个问题通常比与大堆有关的问题要糟糕得多:每次Solr想要为操作分配内存时,它都必须执行完整的垃圾回收才能释放足够的内存来完成分配。 完整的垃圾回收会暂停JVM,并且它们非常缓慢。 如果堆大小和GC调整良好,那么这样的完整集合非常罕见。
要求太多的行
用数百万行索要数百行rows = 9999999结合高查询率是一种已知的组合,在中等规模的索引(5-10mill)上也可能导致大量完整的GC问题。即使实际点击次数非常低,客户端请求大量行的事实将导致分配大量Java对象(每行请求一个ScoreDoc),并且还保留有价值的RAM(每行28个字节)。因此,要求使用高行参数的“所有”文档不是免费的。您将看到许多垃圾回收正在进行,并且内存消耗不断增加,直到触发完整的GC。增加堆有时可能会有所帮助,但最终会导致长时间停顿,所以我们需要解决根本问题。阅读Toke Eskildsen的博客文章,了解问题的细节和一些改进建议。
简单的解决方案是请求更少的行,或者如果您需要获得大量文档,请切换到/ export或cursorMark(或流式传输)。
如果您无法控制客户端,则可以尝试在solrconfig的不变式部分设置行,或者如果需要动态设置,可以通过自定义SearchComponent(例如RequestSanitizerComponent)允许的最大值设置上限。
工具和垃圾收集
除非问题是由于堆太小造成的,否则JVisualVM和JConsole等工具不会显示您在GC暂停时遇到问题。 您只能看到有关总计和平均值的信息。
以下免费工具擅长揭示暂停问题。 可能有更多的工具可用:
jHiccup
GCLogViewer
GCViewer
SSD
固态盘很棒。它们具有很高的传输速率,几乎可以消除与随机访问数据相关的延迟问题。
购买高级磁盘前需要考虑一个细节:如果您有足够的备用内存来提供良好的磁盘缓存,磁盘的速度对大多数查询的性能几乎没有影响。如果没有足够的内存用于良好的缓存,则磁盘速度可能会有所不同,但添加内存通常会提高性能。
在内存不足的情况下,如果将索引放置在固态硬盘上,性能将优于具有标准旋转磁盘的类似硬件。有时候性能的提升将会非常巨大,但是如果你正在研究SSD,因为你的Solr服务器存在严重的性能问题,那么切换到SSD的可能性就不会如你所期望的那么大。
通常SSD会被吹捧为用作磁盘缓存的RAM的替代品。这是真实的和不真实的。尽管SSD的速度令人难以置信,但内存(操作系统磁盘高速缓存)仍然快得多,内存在基于SSD的系统性能方面仍扮演着重要角色。与旋转磁盘相比,您可能不需要使用固态硬盘的RAM,但无法消除这一要求。使用旋转磁盘时,需要索引大小的50%到100%作为缓存。如果您的索引非常小,SSD可能会达到25%到50%。
请注意,SSD仍然是一项年轻的技术,独立的面向Solr的性能测试的数量非常有限。其中一项测试表明,只有10%索引大小的磁盘缓存可能足以实现SSD的高搜索性能。请参阅内存已过高。请注意,如果您的索引存储的字段非常少,则10%可能不够。如果你有很多(或非常大)存储的字段,可能是。通常,找出硬件适当大小的唯一方法是实际建立一个系统并尝试。
SSD的一个潜在问题是需要操作系统TRIM支持以实现良好的长期性能。对于单磁盘,TRIM通常得到很好的支持,但是如果您想添加任何类型的硬件RAID(以及大多数软件RAID),则TRIM支持将消失。在编写本文时,似乎只有英特尔支持一种解决方案,并且仅限于Windows 7或更高版本以及RAID 0.一种使Solr的问题更少的方法是将您的操作系统和Solr本身置于RAID的常规磁盘,并将您的索引数据放在单独的SSD上。如果SSD发生故障,您的冗余服务器仍然会在那里处理请求。
极端缩放
当Solr索引达到数十亿文档和数百GB时,硬件要求开始变得极端,特别是*如果查询率很高。 处理这种大小的索引变得非常昂贵,因为它通常需要很多服务器,每个服务器都有很多内存。 SSD变得更加关键,这导致成本上涨。
这部分需要扩展和从极端规模的实际部署中获取信息。
慢启动
导致启动速度缓慢有两个主要原因。 一个与事务日志有关,另一个与Suggester组件有关。
由于事务日志缓慢启动
尽管可能还有其他原因,但这个问题最常见的原因是Solr4.0中引入的updateLog功能。问题不在于功能本身,而是取决于打开功能时如何配置和使用Solr的其他部分,事务日志可能会失去控制。
updateLog功能为所有更新添加事务日志。正确使用时,事务日志是一件好事,它是SolrCloud所必需的。该版本还介绍了软提交的概念。
如果您发送大量文档更新到您的索引而没有进行任何提交或仅进行软提交,那么事务日志将变得非常大。 Solr启动时,将重播整个事务日志,以确保索引更新不会丢失。对于非常大的原木,这非常缓慢。大日志也可能由使用DataImportHandler的大量导入引起,后者可选地在最后进行硬提交。
要修复慢速启动,您需要保持事务日志大小不变。做到这一点的唯一方法是频繁发送硬提交。硬提交会关闭当前的事务日志并启动一个新的事务日志。 Solr只保留足够的事务日志,以便能够恢复索引的最后100个文档,但它不能拆分单个日志文件,因此如果最新的日志文件非常大,则必须保留整个文件并重播它在启动时。重放小型事务日志很快,因此保持它们非常小(仅有几百或几千更新)是首选。
在solrconfig.xml更新处理程序定义中打开autoCommit是解决方案:
<updateHandler class="solr.DirectUpdateHandler2">
<autoCommit>
<maxDocs>25000</maxDocs>
<maxTime>300000</maxTime>
<openSearcher>false</openSearcher>
</autoCommit>
<updateLog />
</updateHandler>
人们发送大量更新而不进行任何提交的一个原因是,他们不希望删除或更新在完成之前可见。 该要求由上述配置中的openSearcher = false设置维护。 如果您使用此选项,则需要发送明确的硬性或软性提交以使更改可见。
您需要调整autoCommit配置中的maxDocs和maxTime参数以适应您的要求。 提供的值(25000个文档或5分钟)是很好的通用默认值,但它们可能需要在更新量非常高或非常低的情况下进行调整.
由于建议组件而启动缓慢
如果索引很大,并且在solrconfig.xml中包含了建议组件,则这可能会导致极长的启动时间。 从4.10.2版本开始,此组件在主要示例配置中默认启用。
描述问题的一个问题是SOLR-6679,在4.10.3中修复。 SOLR-6845在5.1版本中也针对此问题进行了更改。
该修复包括编辑配置以删除或评论提示器组件,就像在此Heliosearch提交中所做的那样。
慢提交
通常只会提交打开新搜索器的速度会很慢。提交时间较慢的主要原因包括:
- Solr缓存上的大型autowarmCount值。
- 非常频繁的提交。
- 上面讨论的磁盘缓存没有足够的操作系统内存。
如果你的Solr缓存中有很大的autowarmCount值,那么缓存变暖可能需要很长时间。 filterCache预热特别慢。解决方案是减少autowarmCount,降低查询的复杂性,或者两者兼而有之。
如果您提交的频率非常高,您可能会在前一个提交完成之前发送新的提交。如果您刚刚讨论过启用了高速缓存预热,则这是更大的问题。如果你的solrconfig.xml文件中有很多maxWarmingSearchers,那么你可能会得到很多新的搜索者同时变暖,这是I / O密集型的,所以这个问题会复杂化。
如果在不打开新的搜索器时遇到缓慢的提交时间问题,那么这可能是由于一般性能问题,如极端的GC暂停或用于磁盘缓存的操作系统内存不足。这两个问题都在本页的前面讨论过。
慢索引
有很多原因导致索引缓慢。 大多数情况下,Solr不会很慢。 索引缓慢的最大原因是从源系统检索信息的速度。
导致索引缓慢的其他可能的问题包括在每次更新请求之后提交,在每个更新请求中一次发送一个文档而不是批量处理,并且仅使用一个线索/连接进行索引。 这些是Solr外部的问题。 可能的解决方法是使用IgnoreCommitOptimizeUpdateProcessorFactory忽略来自客户端的所有提交,而是设置autoCommit。
更多帮助
如果您需要本页面讨论的任何问题的其他帮助,Solr有一个非常活跃的社区。 在寻求帮助之前,请确保您可以提供相关信息。
其他说明
通过google翻译有所更改,翻译自:https://wiki.apache.org/solr/SolrPerformanceProblems