【问题标题】:Hibernate-Search flushToIndexes causing java.lang.OutOfMemoryError (heap space)Hibernate-Search flushToIndexes 导致 java.lang.OutOfMemoryError(堆空间)
【发布时间】:2018-12-11 18:04:43
【问题描述】:

我有一个 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 配置来避免这种行为?

【问题讨论】:

    标签: java hibernate-search


    【解决方案1】:

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

    它还没有解决,因为问题相当复杂。对于@ContainedIn (HSEARCH-1937),我们需要使用查询而不是关联,这将相当简单。但更重要的是,我们需要执行分块(定期刷新/清除),这要么对用户会话产生副作用,要么在用户事务之外执行 (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 的工作,但它甚至适用于非常大的表,并且数据库更改和重新索引之间的延迟会更短。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-24
      • 2021-09-09
      • 2017-05-13
      • 1970-01-01
      • 2017-04-22
      • 2011-11-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多