【问题标题】:SOLR Out Of Memory Error when reading/indexing a large index读取/索引大索引时出现 SOLR 内存不足错误
【发布时间】:2023-03-26 06:15:01
【问题描述】:

在索引大量数据时,我遇到了 Solr 的 OOM 错误。我知道一般建议是将索引拆分为分片,但实际上情况已经如此。我正在索引分片,此时进一步拆分不是一种选择。我想了解发生了什么以及为什么会出现此错误,以及除了拆分或提供更多 RAM 之外,我还能做些什么。

如果在这种情况下 RAM 消耗是线性的(或更糟),我会很难过,我宁愿让它亚线性。

情况是我正在用随机字符串索引文档(因此字典非常非常大)。每个文档有几个 20-30 个字符的字段和一个大约 200-500 个字符的字段。每个分片中的索引大小约为 250-260GB,每个处理该索引的 solr 实例大约有 4GB 内存。当 OOM 发生时,在重新启动后 Solr HeapDump 看起来大致相同,因此它可能与索引无关,而是与 Solr Searcher 相关。就在 OOM 之前,堆转储的最大对象如下所示:

<tree type="Heap walker - Biggest objects">
  <object leaf="false" class="org.apache.solr.core.SolrCore" objectId="0xf02c" type="instance" retainedBytes="120456864" retainedPercent="97.4">
    <outgoing leaf="false" class="org.apache.solr.search.SolrIndexSearcher" objectId="0xfb52" type="instance" retainedBytes="120383232" retainedPercent="97.3" referenceType="not specified" referenceName="[transitive reference]">
      <outgoing leaf="false" class="org.apache.lucene.index.ReadOnlySegmentReader" objectId="0x1018e" type="instance" retainedBytes="8161688" retainedPercent="6.6" referenceType="not specified" referenceName="[transitive reference]"/>
      <outgoing leaf="false" class="org.apache.lucene.index.ReadOnlySegmentReader" objectId="0x10185" type="instance" retainedBytes="8148072" retainedPercent="6.6" referenceType="not specified" referenceName="[transitive reference]"/>
      <outgoing leaf="false" class="org.apache.lucene.index.ReadOnlySegmentReader" objectId="0x10188" type="instance" retainedBytes="8138232" retainedPercent="6.6" referenceType="not specified" referenceName="[transitive reference]"/>
      <outgoing leaf="false" class="org.apache.lucene.index.ReadOnlySegmentReader" objectId="0x10186" type="instance" retainedBytes="8129160" retainedPercent="6.6" referenceType="not specified" referenceName="[transitive reference]"/>
      <outgoing leaf="false" class="org.apache.lucene.index.ReadOnlySegmentReader" objectId="0x10191" type="instance" retainedBytes="8124608" retainedPercent="6.6" referenceType="not specified" referenceName="[transitive reference]"/>
      <outgoing leaf="false" class="org.apache.lucene.index.ReadOnlySegmentReader" objectId="0x1018a" type="instance" retainedBytes="8123144" retainedPercent="6.6" referenceType="not specified" referenceName="[transitive reference]"/>

      <outgoing leaf="false" class="org.apache.lucene.index.ReadOnlySegmentReader" objectId="0x10192" type="instance" retainedBytes="8100904" retainedPercent="6.5" referenceType="not specified" referenceName="[transitive reference]"/>
      <outgoing leaf="false" class="org.apache.lucene.index.ReadOnlySegmentReader" objectId="0x10190" type="instance" retainedBytes="8097984" retainedPercent="6.5" referenceType="not specified" referenceName="[transitive reference]"/>
      <outgoing leaf="false" class="org.apache.lucene.index.ReadOnlySegmentReader" objectId="0x1018b" type="instance" retainedBytes="8096160" retainedPercent="6.5" referenceType="not specified" referenceName="[transitive reference]"/>
      <outgoing leaf="false" class="org.apache.lucene.index.ReadOnlySegmentReader" objectId="0x1018d" type="instance" retainedBytes="8081656" retainedPercent="6.5" referenceType="not specified" referenceName="[transitive reference]"/>
      <outgoing leaf="false" class="org.apache.lucene.index.ReadOnlySegmentReader" objectId="0x10187" type="instance" retainedBytes="8042504" retainedPercent="6.5" referenceType="not specified" referenceName="[transitive reference]"/>
      <outgoing leaf="false" class="org.apache.lucene.index.ReadOnlySegmentReader" objectId="0x1018c" type="instance" retainedBytes="8039336" retainedPercent="6.5" referenceType="not specified" referenceName="[transitive reference]"/>
      <outgoing leaf="false" class="org.apache.lucene.index.ReadOnlySegmentReader" objectId="0x10189" type="instance" retainedBytes="8036952" retainedPercent="6.5" referenceType="not specified" referenceName="[transitive reference]"/>
      <outgoing leaf="false" class="org.apache.lucene.index.ReadOnlySegmentReader" objectId="0x1018f" type="instance" retainedBytes="7948568" retainedPercent="6.4" referenceType="not specified" referenceName="[transitive reference]"/>
      <outgoing leaf="false" class="org.apache.lucene.index.ReadOnlySegmentReader" objectId="0x10195" type="instance" retainedBytes="832448" retainedPercent="0.7" referenceType="not specified" referenceName="[transitive reference]"/>

      <outgoing leaf="false" class="org.apache.lucene.index.ReadOnlySegmentReader" objectId="0x10196" type="instance" retainedBytes="830584" retainedPercent="0.7" referenceType="not specified" referenceName="[transitive reference]"/>
      <outgoing leaf="false" class="org.apache.lucene.index.ReadOnlySegmentReader" objectId="0x10194" type="instance" retainedBytes="829232" retainedPercent="0.7" referenceType="not specified" referenceName="[transitive reference]"/>
      <outgoing leaf="false" class="org.apache.lucene.index.ReadOnlySegmentReader" objectId="0x10197" type="instance" retainedBytes="828808" retainedPercent="0.7" referenceType="not specified" referenceName="[transitive reference]"/>
      <outgoing leaf="false" class="org.apache.lucene.index.ReadOnlySegmentReader" objectId="0x10198" type="instance" retainedBytes="827312" retainedPercent="0.7" referenceType="not specified" referenceName="[transitive reference]"/>
      <outgoing leaf="false" class="org.apache.lucene.index.ReadOnlySegmentReader" objectId="0x10199" type="instance" retainedBytes="824736" retainedPercent="0.7" referenceType="not specified" referenceName="[transitive reference]"/>
      <outgoing leaf="false" class="org.apache.lucene.index.ReadOnlySegmentReader" objectId="0x1019a" type="instance" retainedBytes="822608" retainedPercent="0.7" referenceType="not specified" referenceName="[transitive reference]"/>
      <outgoing leaf="false" class="org.apache.lucene.index.ReadOnlySegmentReader" objectId="0x10193" type="instance" retainedBytes="783424" retainedPercent="0.6" referenceType="not specified" referenceName="[transitive reference]"/>
      <cutoff objectCount="96" totalSizeBytes="534976" maximumSingleSizeBytes="87560"/>
    </outgoing>

    <cutoff objectCount="53" totalSizeBytes="73496" maximumSingleSizeBytes="40992"/>
  </object>
  <object leaf="false" class="org.mortbay.jetty.webapp.WebAppClassLoader" objectId="0xdf88" type="instance" retainedBytes="420208" retainedPercent="0.3"/>
  <object leaf="false" class="org.apache.solr.core.SolrConfig" objectId="0xe5f5" type="instance" retainedBytes="184976" retainedPercent="0.1"/>
 ..... 

jmap 简单转储如下所示:

Attaching to process ID 27000, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 20.5-b03

using thread-local object allocation.
Parallel GC with 2 thread(s)

Heap Configuration:
   MinHeapFreeRatio = 40
   MaxHeapFreeRatio = 70
   MaxHeapSize      = 268435456 (256.0MB)
   NewSize          = 1310720 (1.25MB)
   MaxNewSize       = 17592186044415 MB
   OldSize          = 5439488 (5.1875MB)
   NewRatio         = 2
   SurvivorRatio    = 8
   PermSize         = 21757952 (20.75MB)
   MaxPermSize      = 85983232 (82.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 31719424 (30.25MB)
   used     = 17420488 (16.61347198486328MB)
   free     = 14298936 (13.636528015136719MB)
   54.92056854500258% used
From Space:
   capacity = 26673152 (25.4375MB)
   used     = 10550856 (10.062080383300781MB)
   free     = 16122296 (15.375419616699219MB)
   39.55608995892199% used
To Space:
   capacity = 27000832 (25.75MB)
   used     = 0 (0.0MB)
   free     = 27000832 (25.75MB)
   0.0% used
PS Old Generation
   capacity = 178978816 (170.6875MB)
   used     = 168585552 (160.7757110595703MB)
   free     = 10393264 (9.911788940429688MB)
   94.19302002757689% used
PS Perm Generation
   capacity = 42008576 (40.0625MB)
   used     = 41690016 (39.758697509765625MB)
   free     = 318560 (0.303802490234375MB)
   99.24167865152106% used

我在这里看不到任何可以为我提供有关如何处理它的任何线索,除了只是提供更多 RAM,在一般情况下这不是解决方案,我想知道发生了什么,为什么 Searcher 和它的 ReadOnlySegmentReaders 会占用所有内存,他们真的必须这样做吗?我可以做点什么吗?

更新: 我用大约 15 万字(不是随机字)的小得多的字典进行了测试,我达到了大约 350GB 的索引大小并且没有 OOME,所以这与索引大小没有直接关系,可能必须这样做更多与术语向量大小(唯一术语)有关。但我仍然想了解我的限制以及如何解决这些限制。

【问题讨论】:

  • 您使用的是什么操作系统?是64位还是32位?
  • 更新:我已经用大约 15 万字(不是随机字)的小得多的字典进行了测试,我达到了大约 350GB 的索引大小并且没有 OOME,所以这不是与索引大小直接相关,可能与术语向量大小(唯一术语)有关。但我仍然想了解我的限制以及如何解决这些限制。
  • 您是否使用随机字符串作为测试数据?如果是这样,您可能想要使用幂律 (Zipf) 分布而不是平坦分布。 Solr 的许多算法都针对文本中常见的分布进行了优化。测试查询也是如此,查询的平坦分布会导致缓存命中率低得不切实际。
  • @WalterUnderwood - 很有趣。是的,我使用的是随机字符串,现在在主流项目中我使用的是真实世界的文本生成器,我没有这些问题,但是在一个分支中我仍然在探索随机字符串版本,以便发现和理解Solr 对我的系统施加的限制。所以目前我并不关心缓存命中率,而不是理解为什么这样的数据语料库会导致整个事情在 OOME 中崩溃。

标签: performance memory solr out-of-memory


【解决方案1】:

您可以自行决定在服务器场的每个“分片”上为所有文档编制索引。分布式索引没有开箱即用的支持,但您的方法可以像循环技术一样简单:将每个文档索引到循环中的下一个服务器。一个简单的散列系统也可以工作,Solr Wiki 建议 uniqueId.hashCode() % numServers 作为适当的散列函数。

请记住,Solr 不会计算通用术语/文档频率。在大范围内,在分片级别计算 tf/idf 可能并不重要 - 但是,如果您的集合在其跨服务器的分布中严重倾斜,您可能会对相关性结果产生疑问。最好将文档随机分发到您的分片。 注意>>>>>>> 尝试使用哈希码而不是随机字符串来索引文档

【讨论】:

  • 我想你没有理解我的问题。我正在索引分片。它使用 Hadoop 并行完成。考虑到我提到的特定文本语料库,这些分片在索引这些数据时崩溃,每个分片的索引大小约为 260GB,我现在已经知道这个 OOM 没有直接连接到索引大小,因为我索引了一些其他数据(不是随机字符串),它给了我一个 360GB 的索引并且碎片幸存下来......无论如何,我猜你正在回答一个不同的问题,无论如何感谢你的兴趣;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-12-02
  • 1970-01-01
  • 2012-02-21
  • 1970-01-01
  • 2017-10-03
  • 2021-06-11
  • 1970-01-01
相关资源
最近更新 更多