Solr性能问题及解决方式[译]

Posted 架构师日常笔记

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Solr性能问题及解决方式[译]相关的知识,希望对你有一定的参考价值。

0.简介

本文将尝试回答以下问题:

  • 为什么Solr性能这么差?

  • 为什么Solr需要这么长时间才能启动?

  • 为什么当我的服务器正常时,SolrCloud却表现得像在故障一样?

这是仅仅只提供基本信息,更多信息请参考相关文档。大纲如下

  • 一般信息

        SolrCloud

  • 请求率高

  • 内存

       操作系统磁盘高速缓存

            特别注意优化操作

  • Java堆

         我需要多少堆空间?

         减少堆需求

        GC暂停的问题

             要求太多的行数

            工具和垃圾收集

     固态硬盘

          在内存/性能问题上寻求帮助

           在POSIX操作系统上列出进程

           在Windows上列出进程

极端的扩展

慢启动

    由于事务日志,启动缓慢

    由于使用了提示者组件,导致启动缓慢

    缓慢的提交

    缓慢的索引

进一步的帮助


1.通用信息

     在6.4.0和6.4.1版本中有一个性能bug,使得所有的东西都变慢了。这个问题在6.4.2中已经修复。SOLR-10130对此进行了描述。这是高度特定于版本的,所以如果您没有运行受影响的版本,不必担心。本文档除这一段外的其余部分并不特定于任何版本的Solr。

Solr性能的一个主要驱动因素是RAM。Solr需要足够的内存用于两个独立的事情:一个是Java堆,另一个是OS磁盘缓存的“空闲”内存。

另一个问题的潜在来源是非常高的查询率。增加内存有时可以让Solr处理更高的速率。如果需要更多的查询可伸缩性,最终这是不够的,您将需要在多台机器(最好是单独的物理硬件)上添加索引的多个副本,以进一步扩展。对于独立的Solr实例,或者当使用SolrCloud与不支持云感知的客户端时,多个副本可能需要一个负载均衡器。

强烈建议Solr运行在64位Java上。64位Java需要64位操作系统,64位操作系统需要64位CPU。32位的软件或硬件没有什么问题,但是32位的Java被限制在2GB的堆。如果堆不能大于2GB,那么很容易构建一个完全不起作用的索引。Java堆将在本页后面的部分讨论。

SolrCloud

 1. Solr 4.x中随着集合数量出现稳定性问题。无论节点的数量或可用资源的数量如何,SolrCloud在集合数量接近数百时就开始出现稳定性问题。对于数千个集合,任何小问题或对集群的更改都可能导致数十分钟内无法恢复的稳定死亡螺旋。尽量将集合的数量保持在较低的水平。这些问题是由于SolrCloud更新ZooKeeper中的集群状态来响应集群的变化。目前正在努力改善这种状况。这个问题出现在Solr 4.x中,集群状态保存在一个单独的clusterstate.json文件。后续的Solr版本(5x及以上)默认情况下将每个集合的数据存储在单独的state.json中,作为每个集合的znode的子节点(例如:/collections/my_collection/state.json)。如果从安装Solr 4x开始,MIGRATESTATE命令将更改为更新的、更可伸缩的状态。也就是说,Zookeeper的负载肯定会随着集合(和副本)数量的增加而增加。最近的Solr版本在拥有数千个副本的情况下表现良好。

2.增加超时时间会提升稳定性。因为SolrCloud严重依赖于ZooKeeper,如果你有潜在的性能问题,导致操作花费的时间超过zkClientTimeout,那么它可能非常不稳定。增加超时时间会有所帮助,但解决底层性能问题会产生更好的结果。默认的超时(在内部是15秒,在最近的示例配置中是30秒)相当长,对于一个经过良好调优的SolrCloud安装来说已经足够了。

3.配置读性能更好的单独zookeeper集群。ZooKeeper的设计假设它能够以极快的速度访问zk数据库。如果ZooKeeper数据库存储在与Solr数据相同的磁盘上,那么Solr的任何性能问题都会延迟ZooKeeper对自己数据库的访问。这可能导致性能死亡螺旋,每次ZK超时都会导致恢复操作,从而导致进一步的超时。ZooKeeper将数据库保存在Java堆内存中,因此磁盘读性能并不像磁盘写性能那么重要。当OS磁盘缓存对于Solr的需求来说太小,并且ZK数据库和Solr数据在同一个磁盘上时,对Solr的大量磁盘访问可能会干扰ZK的写操作。为ZK使用非常快的磁盘(特别是SSD)将带来良好的性能。强烈建议为Solr和ZK数据使用单独的物理磁盘。为所有ZK节点(至少需要三个节点以实现冗余)提供专用的机器更好,但这并不是严格的要求。

2.请求率过高

     如果您的请求率很高,这将对性能产生影响,通常影响非常大。确定什么是“高”并不是一件简单的事情。有些服务器可以轻松地每秒处理200个请求,有些服务器则很难每秒处理10个请求。如果您的请求率超过30 / s,那么可能是时候考虑通过添加索引的额外副本和额外的服务器来扩展安装了。每秒处理数千个请求需要大量的硬件。

处理高查询率通常需要在多个服务器上建立索引的多个副本,并实现某种负载平衡。SolrCloud可以自动化很多这方面的工作,它在内部实现负载平衡,但您可能仍然需要一个外部负载平衡器。

3.RAM

1.操作系统磁盘高速缓存

对于索引更新,Solr依赖于快速的大容量读写。对于搜索,快速的随机读取是必不可少的。满足这些需求的最佳方法是确保有一个大型磁盘缓存可用。您还可以利用固态驱动器来加速Solr,但请注意,这并不能完全替代OS磁盘缓存。详细信息请参见本文档后面的SSD部分。

简而言之,您希望在操作系统磁盘缓存中有足够的可用内存,以便索引的重要(经常访问的)部分能够装入缓存。假设您的Solr索引大小为8GB。如果您的操作系统、Solr的Java堆和所有其他正在运行的程序需要4GB内存,那么该服务器的理想内存大小至少是12GB。您可能可以让它在8GB的总内存下工作(剩下4GB用于磁盘缓存),但这可能也不够。真正重要的事情是确保操作系统磁盘缓存有一个高缓存命中率…不是为了达到完美。

如果您需要尽可能低 整体查询延迟,那么最好的办法是拥有足够的系统内存来完全缓存整个索引。如果您不需要最小的延迟,缓存整个索引可能是没有必要的。

确切的最低要求是高度可变的,取决于模式、索引内容和查询等。如果您的索引有很多存储字段,那么这些需求将是规模较小的一端。如果您的存储数据非常少,那么您将希望在规模的高端。存储数据的大小不会对搜索速度产生太大影响,尽管它可能会影响在确定所需文档后检索搜索结果所需的时间。

(警告)是非常重要的注意,没有快速公式用于决定性能良好所需的最少的内存。建立一个完整的系统可能是你能知道的唯一方法。有时,一个小规模的原型可以揭示有用的信息。

特别注意优化操作

优化索引会导致大量的磁盘I/O。如果没有足够的OS磁盘缓存来有效地缓存索引,那么一个优化操作可能会对正常的Solr操作造成*非常*的破坏。如果优化是经常发生的事情,那么可能需要有足够的RAM来缓存至少100%的索引内容。如果您有足够的RAM同时缓存索引的原始版本和优化版本,那么优化期间的性能将是最好的。如果主服务器上发生了大型合并或优化,这个说明也适用于从服务器进行复制。

2.Java Heap

java堆是java程序(如Solr)实际运行所需的内存。

如果您在日志中看到OutOfMemoryError (OOME)异常,您可能会在这里结束。如果您看到了这种情况,那么您的Solr安装需要比允许访问的资源更多的资源。对付OOME只有两种选择。一种是增加正在耗尽的资源的大小,另一种是减少Solr需要的资源数量。通常是堆内存触发OOME,但也可能是其他资源,如允许运行的线程/进程的数量。有些人可能会说还有第三种选择:修复程序中需要大量资源的bug。这并不是第三种选择——它属于第二种。

Solr中的某些配置和条件将需要大量的堆内存。以下列表是不完整的,但没有特别的顺序,包括:

  • 一个大型索引。

  • 频繁的更新。

  • 超级大型文档。

  • 广泛使用faceting。

  • 使用很多不同的排序参数。

  • 非常大的Solr缓存

  • 一个大RAMBufferSizeMB。

  • 使用Lucene的RAMDirectoryFactory。

我需要多少堆空间?

简而言之:这是一个没有通用答案的问题。您想要一个足够大的堆,这样您就不会遇到一些异常和持续垃圾收集的问题,但又要足够小,这样您就不会浪费内存或遇到巨大的垃圾收集暂停。不要遵循那些告诉你使用总内存大小的特定部分(四分之一,一半,等等)来堆的建议。如果遵循这个建议,很容易得到一个太小或太大的堆大小。

长话短说:你得试验一下。Java开发工具包(JDK)附带了两个GUI工具(jconsole和jvisulavm),您可以将它们连接到正在运行的Solr实例,并查看随着时间的推移使用了多少堆。对于长期的JVM堆、内存空间和垃圾收集监视,可以使用SPM等工具。关于JVM内存池监视的文章展示了在内存池报告中寻找哪些内容以避免OOME。

这个jconsole示例中的图表显示了一个典型的锯齿状模式——内存使用量达到峰值,然后垃圾收集释放了一些内存。确定多少集合太多取决于您的查询/更新量。一个可能的经验法则是:查看Solr每秒看到的查询数。如果每分钟的垃圾收集次数超过该值,则堆可能太小。也可能完全没问题……调优的垃圾收集可能会频繁地执行大量非常快速的收集。

如果您让Solr服务器在高查询和更新负载下运行,锯齿模式中的低点将绝对代表所需内存的最小值。尝试将最大堆设置在该值的125%到150%之间,然后重复监视,看看锯齿形模式中的低点是否明显高于以前,或者垃圾收集是否频繁发生。如果是,则使用更高的最大堆重复测试。

另一种确定您对堆大小的猜测是否正确的方法是获取Solr所写的GC日志并对其进行分析。gceasy网站让这一分析变得相当容易。如果您还不知道要查找什么,可以与Solr的支持资源之一共享您的GC日志,以获得一些帮助。

额外的经验法则:更多的堆通常是更好的,但是如果你把它设置得太大,花费在垃圾收集上的时间会变得非常多。这个问题将在下面讨论。另外,如上所述,减少操作系统磁盘缓存的大小可能会产生更多的问题。

减少堆需求

调优垃圾收集参数并不能减少Solr所需的内存量!它可能会使内存回收更有效,但它绝对不能降低程序所需的内存量。它可能使锯齿形记忆图中的高点更低,但它不会对低点做任何事情。如果遇到OutOfMemory错误,对GC调优进行更改将没有帮助。更有效的垃圾收集可能会导致程序在OOME发生之前花费更长的时间,但它不能阻止OOME发生。

以下是一个不完整的列表,没有特定的顺序,关于如何减少堆的要求,基于上面的列表,需要大量堆的事情:

  • 使用一个大的索引,并将其分布——将索引分成多个碎片。

         一个非常简单的方法是切换到SolrCloud。您可能需要重新索引,但SolrCloud将为您处理所有分片。这实际上并没有减少大索引的总体内存需求(实际上可能会略微增加),但是分片索引可以分布在多个服务器上,每个服务器的内存需求较低。为了实现冗余,不同的服务器上应该有多个副本。

       如果查询速率很低,那么将多个分片放在一台服务器上将会有很好的性能。随着查询速率的增加,每个服务器只拥有一个碎片副本变得非常重要。

  • 不要存储所有的字段,特别是那些非常大的字段。

      相反,让您的应用程序从原始数据源(而不是Solr)检索详细数据。

      注意,这样做将意味着您不能使用原子更新。

  • 您还可以在用于排序/facet和重新索引的字段上启用docvalue。

  • 减少不同排序参数的数量。就像facet一样,docValues可以对性能和排序时的内存使用产生积极的影响。

  • 减少Solr缓存的大小。

  • 减少RAMBufferSizeMB。在最近的Solr版本中,默认值是100。

       如果有很多核,这个值可能特别重要,因为每个核都会使用一个缓冲区。

  • 不要使用RAMDirectoryFactory——相反,使用默认值并安装足够的系统RAM,这样操作系统就可以像上面讨论的那样缓存整个索引。 

GC暂停的问题

当您有一个很大的堆(大于2GB)时,GC暂停可能是一个主要问题。这通常是由偶尔需要的FULL-GC引起的,它必须“停止整个世界”——暂停所有程序执行以清理内存。有两种主要的解决方案:一种是使用商业上的低暂停JVM,比如Zing,这是有价格标签的。另一种方法是调优您已经拥有的自由JVM。GC调优是一种艺术形式,适用于某个人的方法可能不适用于您。

对于Solr来说,使用带有调优参数的ConcurrentMarkSweep (CMS)收集器是一个非常好的选择,但是对于最新的Java 7版本(撰写本文时为7u72),如果使用-XX:+ParallelRefProcEnabled选项,G1似乎是一个更好的选择。来自Oracle GC专家的信息表明,最新的Java 8将显著提高G1的性能,但这还没有得到证实。下面是一些建议,希望对你有所帮助:

  • 作者的GC调优参数

  • G1调优技巧

  • Oracle Java 6 GC调优指南

对于CMS,手动调优各种堆生成的大小非常重要。G1收集器在运行时自动调整代的大小,强制调整大小通常会导致性能下降。

如果您的最大堆有点太小,那么您可能会遇到稍微不同的垃圾收集问题。这个问题通常比与大堆相关的问题要严重得多:每次Solr想为操作分配内存时,它都必须进行一次FULL-GC,以便释放足够的内存来完成分配。FULL-GC将暂停JVM,而且它们非常缓慢。如果堆大小和GC都得到了很好的调优,那么这样的FULL-GC是非常罕见的。

要求太多的行数

要求数百万行,例如:rows=9999999,加上高查询速率,这种组合也会在中等大小的索引(5-10mill)上导致大量的完整GC问题。即使实际的命中率非常低,客户端请求大量的行也会导致大量Java对象的分配(每请求一行一个ScoreDoc),并且还会保留有价值的RAM(每一行28字节)。因此,使用高行参数要求“所有”文档并不是免费的。您将看到大量的垃圾收集正在进行,内存消耗不断增加,直到触发完整的垃圾收集。增加堆有时可能会有帮助,但最终会导致长时间的暂停,因此我们需要修复根本问题。阅读Toke Eskildsen关于这个问题的细节和他对改进Solr代码的建议的博客文章。

简单的解决方案是请求更少的行,或者如果你需要获得大量的文档,切换到/export、cursorMark或streaming。

如果您无法控制客户端,则可以尝试在solrconfig的不变量部分中设置行,或者如果需要动态设置行,则可以通过定制的SearchComponent(如requestsanitiercomponent)设置允许的最大行数的上限。

工具和垃圾收集

除非问题是由于堆太小引起的,否则像JVisualVM和JConsole这样的工具不会显示GC暂停有问题。您只能看到总数和平均值的信息。

以下免费工具很擅长揭示暂停问题。可能有更多可用的工具:

  • jHiccup

  • GCLogViewer

  • GCViewer

SSD

固态硬盘是惊人的。它们具有较高的传输速率,并且几乎消除了与随机访问数据相关的延迟问题。

在购买高级磁盘之前,有一个细节需要考虑:如果您有足够的空闲内存用于良好的磁盘缓存,那么磁盘的速度对大多数查询的性能影响很小。如果没有足够的内存来进行良好的缓存,那么磁盘速度会有所不同,但是增加内存通常会提供更好的性能提高。

在内存不够用的情况下,如果将索引放在固态磁盘上,性能将比在具有标准旋转磁盘的类似硬件上更好。有时性能提高会非常大,但如果您因为Solr服务器存在严重的性能问题而选择SSD,那么切换到SSD的帮助可能不会像您预期的那么大。

SSD经常被吹捧为用作磁盘缓存的RAM的替代品。这是真的,也是假的。尽管SSD的速度令人难以置信,但RAM(操作系统磁盘缓存)仍然要快得多,而且RAM仍然在基于SSD的系统的性能中扮演着重要角色。使用SSD可能不像使用旋转磁盘那样需要那么多的RAM,但也不能消除这种需求。对于旋转磁盘,您需要50%到100%的索引大小作为缓存。对于SSD,这个比例可能是25%到50%,如果索引非常小,这个比例会更小。

请注意,ssd仍然是一项年轻的技术,面向solr的独立性能测试的数量非常有限。一个这样的测试表明,只有10%索引大小的磁盘缓存对于ssd的高搜索性能可能已经足够了。内存被高估了。注意,如果索引中存储的字段很少,那么10%可能不够。如果您有很多(或非常大)存储字段,则可能是这样。通常,确定硬件大小是否合适的唯一方法是实际设置一个系统并进行尝试。

SSD的一个潜在问题是,长期良好的性能需要操作系统修剪支持。对于单个磁盘,修剪通常得到很好的支持,但如果您想添加任何类型的硬件RAID(以及大多数软件RAID),修剪支持就消失了。在写这篇文章的时候,似乎只有英特尔支持一个解决方案,而且仅限于Windows 7或更高版本和RAID 0。解决Solr问题的一种方法是将操作系统和Solr本身放在由常规磁盘组成的RAID中,并将索引数据放在单独的SSD上。在正确的Solr设置中,如果SSD失败,冗余服务器仍然会在那里处理请求。

在内存/性能问题上寻求帮助

如果您在与性能有关的问题上寻求帮助,这通常会发生在solr-user邮件列表或#solr IRC频道上,首先需要确定的事情之一是您是否有足够的内存来实现合理的性能。从操作系统获取某些信息将有助于确定这一点。进程信息的屏幕截图(按内存使用情况排序)可以回答邮件列表或IRC频道中的大多数问题。

   在POSIX操作系统上列出进程

这将涉及名为“top”的实用程序。该程序还有其他一些可用的变体,如htop,它们不能提供所需的信息。运行“top”实用程序。如果它是Gnu项目生成的top版本,则可以按shift-M按RES内存降序排序。如果它是top的另一个版本,获得合适的排序可能需要研究。一旦实现了正确的排序,抓取一个截图。将截图共享给文件共享网站。

例如,有28GB堆和超过700gb索引数据:


   在Windows上列出进程

这只适用于最新版本的Windows。希望它将是服务器版,2008年或更新,64位。运行名为Resource Monitor的程序。点击内存选项卡,然后调整窗口和列的大小,以便可以清楚地看到东西。单击“工作集”列,直到排序在该列内降序。抓一个截图。如果你按Alt-PrintScreen,窗口的内容将结束在剪贴板,你可以粘贴到一个程序,如油漆。将截图共享给文件共享网站。

这个截屏示例来自一台实际上没有运行Solr的机器,但除了这个细节,还显示了所需的内容:


4.极端的扩展

当Solr索引达到数十亿个文档和数百gb时,硬件要求开始变得极端,*特别是在查询速度高的情况下*。处理这种大小的索引会变得非常昂贵,因为它通常需要很多服务器,每个服务器都有很多内存。SSD变得更加关键,这推高了成本。

这一节需要从实际的极端规模的部署中扩展和信息。

5.慢启动

导致启动缓慢的主要原因有两个。一个与事务日志相关,另一个与suggester组件相关。

由于事务日志,启动缓慢

尽管可能有其他原因,但最常见的原因是Solr4.0中引入的updateLog特性。问题不在于特性本身,而在于启用该特性时,Solr的其他部分如何配置和使用,事务日志可能会增长到无法控制的程度。

updateLog特性为所有更新添加了一个事务日志。如果正确使用,事务日志是一个好东西,SolrCloud需要它。这个版本还引入了软提交的概念。

如果你发送大量的文档更新到你的索引,而根本不做任何提交,或者只做软提交,事务日志会变得非常非常大。当Solr启动时,将重播整个事务日志,以确保索引更新不会丢失。用非常大的log,这个过程非常缓慢。使用DataImportHandler进行大规模导入也可能导致大量日志,DataImportHandler可以选择在最后执行硬提交。

要解决启动缓慢的问题,您需要减小事务日志的大小。唯一的方法是频繁地发送硬提交。硬提交关闭当前事务日志并启动一个新的事务日志。Solr只保留足够的事务日志,以恢复最后建立索引的100个文档,但它不能分解单个日志文件,因此,如果最新的日志文件非常非常大,它必须保留整个日志文件,并在启动时重放它。重放小事务日志的速度很快,所以最好保持它们非常小(只有几百或几千次更新)。

在solrconfig.xml更新处理程序定义中启用自动提交是解决方案:

<updateHandler class="solr.DirectUpdateHandler2">

<autoCommit>

<maxDocs>25000</maxDocs>

<maxTime>300000</maxTime>

<openSearcher>false</openSearcher>

</autoCommit>

<updateLog />

</updateHandler>

人们在不做任何提交的情况下发送大量更新的一个原因是,他们不希望删除或更新在全部完成之前都是可见的。上述配置中的openSearcher=false设置维护了这个需求。如果使用此选项,则需要发送显式的硬提交或软提交,以使更改可见。

您需要调整maxDocs和maxTime参数在您的自动提交配置,以适应您的需求。提供的值(25000文档或5分钟)是很好的通用默认值,但在更新量非常大或非常小的情况下,它们可能需要调整。

由于使用了suggester组件,导致启动缓慢

如果您有一个很大的索引,并且在您的solrconfig.xml中有suggester组件,这会导致非常长的启动时间。从4.10.2版本开始,该组件在主示例配置中默认启用。

描述这个问题的一个问题是SOLR-6679,在4.10.3中修复了。在5.1版本的SOLR-6845中也对这个问题进行了修改。修复包括编辑配置以删除或注释建议者组件,就像在这个Heliosearch提交中所做的那样。

6.慢提交

通常只有提交打开新搜索器才会很慢。提交时间慢的主要原因包括:

  • Solr缓存上的autowarmCount 。

  • 堆大小的问题。由于堆太大而产生的问题往往是不常见的,而由于堆太小而产生的问题往往会持续发生。

  • 非常频繁的提交。

  • 没有足够的操作系统内存用于磁盘缓存,如上所述。

   如果您的Solr缓存上有很大的自动warmcount值,那么进行缓存预热需要很长时间。filterCache的加热速度特别慢。解决方案是减少autowarmCount,或者减少查询的复杂性,或者两者都要减少。

  如果你频繁的提交,你可能会在之前的提交完成之前发送一个新的提交。如果您像刚才讨论的那样启用了缓存加热,那么这将是一个更大的问题。如果你的solrconfig.xml中有很高的maxWarmingSearchers时,您可能会同时产生许多新的搜索者,这是非常密集的I/O,因此问题会复杂化。

如果你在没有打开一个新的搜索时遇到提交时间慢的问题,那么这可能是由于一般的性能问题。其中一些问题已经在本页前面讨论过了。

7.缓慢的索引

索引速度慢的原因有很多。大多数时候,Solr并不是很慢的。索引速度慢的最大原因是从源系统检索信息的速度。

导致索引速度慢的其他可能的问题包括:在每个更新请求之后提交,在每个更新请求中每次发送一个文档,而不是批处理它们,以及只使用一个线程/连接建立索引。这些都是Solr外部的问题。可能的解决方案是使用IgnoreCommitOptimizeUpdateProcessorFactory来忽略所有来自客户机的提交,而是设置autoCommit。

进一步的帮助

如果您需要额外的帮助来解决这个页面上讨论的任何问题,Solr有一个非常活跃的社区。在寻求帮助之前,确保你能提供相关信息。



以上是关于Solr性能问题及解决方式[译]的主要内容,如果未能解决你的问题,请参考以下文章

Solr简介和使用(一期)

Linux下编译PHP常见错误及解决方法

Solr安装及使用示例

android7.0 编译问题及解决

windows下编译ffmpeg源码及常见问题

Java高性能编程之CAS与ABA及解决方法