java - Hibernate-Search flushToIndexes 导致 java.lang.OutOfMemoryError(堆空间)

标签 java hibernate-search

我有一个 Spring 应用程序,使用 Hibernate,并通过 Hibernate-Search 连接到 Elasticsearch。
为了简化示例,我将只放置必需的注释和代码。

我有一个实体 A,包含在多个 B 实体中(很多,实际上是 ~8000)。
B 实体还包含许多嵌入的详细信息(实体 CE、...)。
这些实体都与 @IndexedEmbedded@ContainedIn Hibernate-Search 注释相关联(请参见下面的示例)。
我创建了一项服务,修改了 A 对象的字段,并通过 flushToIndexes 强制刷新。

刷新时,Hibernate-Search 更新 A 索引,并且由于 @ContainedIn,在 8000 个 B 索引上传播。 但是为了更新 B 索引,由于某种原因,Hibernate-Search 一次加载每 8000 个链接到 A 对象的 B 对象, 以及那些 B 对象(CE 等)中包含的所有详细信息。
所有这一切都需要很长时间,并且只会以 java.lang.OutOfMemoryError: Java heap space 结束。


@Entity
@Table(name = "A")
@Indexed
public class A {

    @ContainedIn 
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "a") 
    private Set<B> bCollection;

    @Field
    @Column(name = "SOME_FIELD")
    private String someField;                            // Value updated in the service
}

@Entity
@Table(name = "B")
@Indexed
public class B {

    @IndexedEmbedded
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "A_ID")
    private A a;

    @IndexedEmbedded
    @OneToOne(fetch = FetchType.LAZY, mappedBy = "b")
    @Fetch(FetchMode.JOIN)  
    private C c;                                         // Some other details

    @IndexedEmbedded
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "b")
    private Set<E> eCollection;                          // Some other details
}

// My service
aObject.setSomeField("some value");
fullTextSession.flushToIndexes();

增加 JVM 分配的内存(从 8GB 到 24GB,这对于约 10000 个对象来说实际上很多)没有解决任何问题。 所以我假设整个数据集的加载需要超过 24 GB...

然而,问题似乎比看起来更复杂~
那是一个错误吗?那很常见吗?我做错了什么 ?我该如何解决?
是否有一些隐藏的 Hibernate-Search 配置来避免这种行为?

最佳答案

这是 Hibernate Search 的一个限制。 @ContainedIn 仅对小型关联表现相对较好;像你这样的大型实体确实会触发所有关联实体的加载,并且性能会很差,或者在最坏的情况下会触发 OOM。

问题比较复杂,目前还没有修复。我们需要为 @ContainedIn ( HSEARCH-1937 ) 使用查询而不是关联,这会相当简单。但更重要的是,我们需要执行分块(定期刷新/清除),这要么对用户 session 产生副作用,要么在用户事务之外执行(HSEARCH-2364),这两者都可能产生不良后果。

解决方法是A.bCollection 上添加@ContainedIn,并手动处理重新索引:https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#manual-index-changes

类似于我在 another answer 中提到的内容,您可以采用以下两种策略之一:

  1. 简单的方法:使用质量索引器定期重新索引所有 B 实体,例如每晚。
  2. 困难路径:每当 A 更改时,将“此实体已更改”的信息保存在某处(这可以像在实体 A 上存储“最后更新日期/时间”一样简单,或者添加事件表中的一行)。同时,有一个周期性的过程检查更改,加载受影响的类型 B 的实体,并重新索引它们。最好以可管理的大小分批进行,如果可以的话,每批处理一个事务(这样可以避免一些麻烦)。

第一个解决方案相当简单,但有一个很大的缺点,即 Person 索引最多会过时 24 小时。根据您的用例,这可能是好的,也可能不是。如果您有许多 B 类型的实体(读作:数百万)并且完全重建索​​引需要的时间超过几分钟,这也可能不可行。

第二种解决方案容易出错,您基本上会执行 Hibernate Search 的工作,但它甚至适用于非常大的表,并且数据库更改和重建索引之间的延迟会短得多。

关于java - Hibernate-Search flushToIndexes 导致 java.lang.OutOfMemoryError(堆空间),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53729914/

相关文章:

java - 此Hibernate Search错误的原因/影响是什么?

indexing - Lucene 计数 - 按查询分组

java - 如何从 .jar 导入 API 定义而不在编译时嵌入 .jar?

java - 在java中 sleep

java - Hibernate 搜索查询?

elasticsearch - 休眠搜索中geo_point的等效数据类型是什么?

java - hibernate 搜索,可以搜索特殊字符吗?

java - Window.location.href 有时不工作的原因是什么?

java - 使用 jni 从 C++ 将 UTF 字符发送到 Java

Java Swing JSplitPane 未按预期显示