Hibernate搜索刷新索引导致java.lang.OutOfMemoryError(堆空间)

Hibernate搜索刷新索引导致java.lang.OutOfMemoryError(堆空间),java,hibernate-search,Java,Hibernate Search,我有一个Spring应用程序,使用Hibernate,并通过Hibernate搜索连接到Elasticsearch。 为了简化这个示例,我将只放置所需的注释和代码 我有一个实体A,包含在多个B实体中(很多,实际上约8000个)。 B实体还包含许多嵌入细节(实体C,E,…)。 这些实体都与@IndexedEmbedded和@ContainedInHibernate搜索注释连接(见下面的示例)。 我创建了一个服务,修改了对象的一个字段,并强制刷新flushToIndexes 在刷新时,Hiberna

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

我有一个实体A,包含在多个B实体中(很多,实际上约8000个)。
B实体还包含许多嵌入细节(实体CE,…)。
这些实体都与@IndexedEmbedded@ContainedInHibernate搜索注释连接(见下面的示例)。
我创建了一个服务,修改了对象的一个字段,并强制刷新flushToIndexes

在刷新时,Hibernate搜索更新A索引,并且由于@ContainedIn,在8000B索引上传播。 但是为了更新B索引,出于某种原因,Hibernate Search会一次加载每8000个链接到A对象的B对象, 以及B对象(CE等)中包含的每个细节 所有这些都需要很长的时间,结果只剩下java.lang.OutOfMemoryError:java堆空间



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

然而,问题似乎比看起来更复杂~
那是虫子吗?这很普遍吗?我做错了什么?我怎样才能解决这个问题?

是否有隐藏的Hibernate搜索配置来避免这种行为?

这是Hibernate搜索的一个限制
@ContainedIn
仅在小型关联中表现相对较好;像您这样的大型实体确实会触发所有相关实体的加载,并且会表现糟糕,或者在最坏的情况下触发OOM

这个问题还没有解决,因为问题相当复杂。对于
@ContainedIn
(),我们需要使用查询而不是关联,这将非常简单。但更重要的是,我们需要执行分块(定期刷新/清除),这要么会对用户会话产生副作用,要么会在用户事务()之外执行,这两者都可能产生恶劣的后果

解决方法是不要在
A.b集合
上添加
@ContainedIn
,而是手动处理重新索引:

与我在中提到的类似,您可以采用以下两种策略之一:

  • 简单方法:使用质量索引器定期重新索引所有
    B
    实体,例如每晚
  • 硬路径:每当
    A
    更改时,将信息“this entity changed”(此实体已更改)保存在某个位置(这可能很简单,如在实体A上存储“上次更新日期/时间”,或在事件表中添加一行)。同时,让一个周期性的过程检查更改,加载类型B的受影响实体,并重新编制它们的索引。最好以可管理的规模批量进行,如果可以的话,每批处理一个事务(这将避免一些麻烦)
  • 第一种解决方案相当简单,但有一个很大的缺点,即
    Person
    索引将过期24小时。根据您的用例,这可能是正确的,也可能不是。如果您有许多类型为B的实体(读取:百万),并且完全重新索引需要几分钟以上的时间,那么这也可能不可行

    第二种解决方案很容易出错,您基本上是在做Hibernate搜索的工作,但即使对于非常大的表,它也可以工作,并且数据库更改和重新索引之间的延迟会短得多

    @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();