gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-30 10:08:31 24 4
gpt4 key购买 nike

我有一个 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/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com