我需要刷新由 SOLR 7.4 管理的索引。我使用 SOLRJ 在具有 8 个 CPU 和 32GB RAM 的 64 位 Linux 机器上访问它(8GB 堆用于索引部分,24GB 用于 SOLR 服务器)。要刷新的索引大小约为 800MB,文档数约为 36k(根据 Luke 的说法)。
在开始索引过程本身之前,我需要“清理”索引并删除与磁盘上的实际文件不匹配的文档(例如:文档之前已被索引并从那时起已移动,因此用户不会如果它出现在结果页面上,则可以打开它)。
为此,我首先需要获取索引中的文档列表:
final SolrQuery query = new SolrQuery("*:*"); // Content fields are not loaded to reduce memory footprint
query.addField(PATH_DESCENDANT_FIELDNAME);
query.addField(PATH_SPLIT_FIELDNAME);
query.addField(MODIFIED_DATE_FIELDNAME);
query.addField(TYPE_OF_SCANNED_DOCUMENT_FIELDNAME);
query.addField("id");
query.setRows(Integer.MAX_VALUE); // we want ALL documents in the index not only the first ones
SolrDocumentList results = this.getSolrClient().
query(query).
getResults(); // This line sometimes gives OOM
当 OOM 出现在生产计算机上时,它会出现在“索引清理”部分,并且堆栈跟踪显示:
Exception in thread "Timer-0" java.lang.OutOfMemoryError: Java heap space
at org.noggit.CharArr.resize(CharArr.java:110)
at org.noggit.CharArr.reserve(CharArr.java:116)
at org.apache.solr.common.util.ByteUtils.UTF8toUTF16(ByteUtils.java:68)
at org.apache.solr.common.util.JavaBinCodec.readStr(JavaBinCodec.java:868)
at org.apache.solr.common.util.JavaBinCodec.readStr(JavaBinCodec.java:857)
at org.apache.solr.common.util.JavaBinCodec.readObject(JavaBinCodec.java:266)
at org.apache.solr.common.util.JavaBinCodec.readVal(JavaBinCodec.java:256)
at org.apache.solr.common.util.JavaBinCodec.readSolrDocument(JavaBinCodec.java:541)
at org.apache.solr.common.util.JavaBinCodec.readObject(JavaBinCodec.java:305)
at org.apache.solr.common.util.JavaBinCodec.readVal(JavaBinCodec.java:256)
at org.apache.solr.common.util.JavaBinCodec.readArray(JavaBinCodec.java:747)
at org.apache.solr.common.util.JavaBinCodec.readObject(JavaBinCodec.java:272)
at org.apache.solr.common.util.JavaBinCodec.readVal(JavaBinCodec.java:256)
at org.apache.solr.common.util.JavaBinCodec.readSolrDocumentList(JavaBinCodec.java:555)
at org.apache.solr.common.util.JavaBinCodec.readObject(JavaBinCodec.java:307)
at org.apache.solr.common.util.JavaBinCodec.readVal(JavaBinCodec.java:256)
at org.apache.solr.common.util.JavaBinCodec.readOrderedMap(JavaBinCodec.java:200)
at org.apache.solr.common.util.JavaBinCodec.readObject(JavaBinCodec.java:274)
at org.apache.solr.common.util.JavaBinCodec.readVal(JavaBinCodec.java:256)
at org.apache.solr.common.util.JavaBinCodec.unmarshal(JavaBinCodec.java:178)
at org.apache.solr.client.solrj.impl.BinaryResponseParser.processResponse(BinaryResponseParser.java:50)
at org.apache.solr.client.solrj.impl.HttpSolrClient.executeMethod(HttpSolrClient.java:614)
at org.apache.solr.client.solrj.impl.HttpSolrClient.request(HttpSolrClient.java:255)
at org.apache.solr.client.solrj.impl.HttpSolrClient.request(HttpSolrClient.java:244)
at org.apache.solr.client.solrj.SolrRequest.process(SolrRequest.java:194)
at org.apache.solr.client.solrj.SolrClient.query(SolrClient.java:942)
at org.apache.solr.client.solrj.SolrClient.query(SolrClient.java:957)
我已经从查询中删除了内容字段,因为已经存在 OOM,所以我认为只存储“小”数据就可以避免 OOM,但它们仍然存在。此外,当我为客户启动该项目时,我们只有 8GB RAM(因此堆为 2GB),然后我们将其增加到 20GB(堆为 5GB),现在增加到 32GB(堆为 8GB),尽管如此,OOM 仍然出现与其他 SO 问题中描述的索引相比(包含数百万个文档),该索引并没有那么大。
请注意,将 800 MB 索引从生产计算机复制到我的开发计算机后,我无法在功能较弱的开发计算机(16GB RAM,因此 4GB 堆)上重现它。
所以对我来说可能存在内存泄漏。这就是为什么我在我的开发机器上使用 800MB 索引关注了Netbeans 关于内存泄漏的帖子。从我所看到的情况来看,我猜想存在内存泄漏,因为在索引之后进行索引,幸存一代的数量在“索引清理”期间不断增加(下面的陡线):
我该怎么办,与索引特性相比,8GB 的堆已经是一个巨大的堆了?因此,增加堆似乎没有意义,因为 OOM 仅在“索引清理”期间出现,而不是在实际索引大型文档时出现,而且它似乎是由幸存的世代引起的,不是吗?创建一个查询对象然后在其上应用
getResults
会对垃圾收集器有所帮助吗?
还有另一种方法来获取所有文档路径吗?或者,即使对于少量文档,逐块检索它们(分页)也可能会有所帮助?
任何帮助表示赞赏
过了一段时间,我终于看到了这篇文章。它准确地描述了我的问题
内存不足 (OOM) 错误通常发生在带有大行参数的查询之后。 Solr 通常会正常工作,直到该查询出现。
所以他们建议(强调是我的):
Solr 的 rows 参数可用于返回超过默认的 10 行。我看到用户成功地将 rows 参数设置为 100-200,但没有看到任何问题。 但是,将 rows 参数设置得较高会产生很大的内存后果,应不惜一切代价避免。
这是我在每页检索 100 个结果时看到的结果:
尽管垃圾收集器的活动更加密集并且计算时间也更长,但幸存代的数量却急剧减少。但如果这是避免 OOM 的成本,那就可以了(看到程序每次索引更新都会损失几秒钟,这可能会持续几个小时)!
将行数增加到 500 已经使内存泄漏再次发生,并且幸存代数增加:
请注意,将行号设置为 200 并不会导致幸存代数增加很多(我没有测量),但在我的测试用例中并没有比“100”表现得好多少(不到 2%) “设置:
这是我用来从索引检索所有文档的代码(来自 Solr 的 wiki):
SolrQuery q = (new SolrQuery(some_query)).setRows(r).setSort(SortClause.asc("id"));
String cursorMark = CursorMarkParams.CURSOR_MARK_START;
boolean done = false;
while (! done) {
q.set(CursorMarkParams.CURSOR_MARK_PARAM, cursorMark);
QueryResponse rsp = solrServer.query(q);
String nextCursorMark = rsp.getNextCursorMark();
doCustomProcessingOfResults(rsp);
if (cursorMark.equals(nextCursorMark)) {
done = true;
}
cursorMark = nextCursorMark;
}
TL;DR :
query.setRows
不要使用太大的数字,即不大于100-200,因为更大的数字很可能会导致OOM。