【问题标题】:Hibernate huge memory allocation with join fetch使用连接提取休眠大量内存分配
【发布时间】:2021-03-31 23:11:55
【问题描述】:

我正在尝试提高应用程序的性能,所以我们做的第一件事就是查看数据库查询以优化它们。

我们做的一件事是在适当的地方添加“join fetch”和“left join fetch”以删除 N+1 个查询。

另一种方法是批量检索结果 (select distinct stuff from stuff left join fetch other_stuff where id in (?1)),而不是 for 循环逐个检索结果 (for (id: ids) select distinct stuff from stuff left join fetch other_stuff where id = ?1)。

问题在于,虽然我们的查询速度更快,数据库负载也更低,但似乎 hibernate 需要更多内存才能做到这一点。

在分析应用程序时,我可以看到每秒分配的对象数超过 500 000。垃圾收集器跟不上,应用程序冻结并最终导致 OutOfMemory。

从 hprof 来看,大多数对象似乎是在休眠类中分配的。最严重的违规者似乎是查询select where id in (?1) 的大部分内容。它每秒执行一次,检索大约 500 个对象,但似乎负责每秒大约 300 000 个对象分配。

我的猜测是,由于连接,数据库返回 300 000+ 行(这确实是我手动运行 select count(*) from stuff join other_stuff 时得到的),hibernate 必须在将所有内容合并到我最后的 500 个对象中之前全部分配。

我说的对吗?

我想不执行 left join fetch 并让休眠使用单独的查询加载它们会大大减少分配,但会损害性能。

有没有办法在立即获取的同时减少内存分配?

谢谢

【问题讨论】:

    标签: java hibernate join memory


    【解决方案1】:

    在 Hibernate 中有 3 种不同的获取策略可供您使用,但只能在查询中指定连接获取。其他的只是注解,在延迟加载时生效。

    • JOIN fetch 在 SQL 连接中可能会导致 N * M 结果集爆炸,就像你想象的那样
    • SUBSELECT 通过嵌入主查询对关联执行一个查询
    • SELECT(默认)为主查询中的每 N 个元素发出一个按 ID 查询的选择,其中 N 是配置的批量大小。如果您的主要查询是非常复杂的查询,因此 SUBSELECT 不合适,这是一个很好的策略。建议将批量大小与您通常期望在主查询中的元素数量保持一致,以避免执行过多的查询。

    通常,我推荐SUBSELECT fetching,除非查询花费的时间太长或者您的主查询产生的元素数量可以估计。

    可以通过减少选择项的数量来实现其他性能改进,但这需要创建自定义 DTO 模型。

    我认为这是Blaze-Persistence Entity Views 的完美用例。

    我创建了该库以允许在 JPA 模型和自定义接口或抽象类定义模型之间轻松映射,例如 Spring Data Projections on steroids。这个想法是您按照自己喜欢的方式定义目标结构(域模型),并通过 JPQL 表达式将属性(getter)映射到实体模型。

    使用 Blaze-Persistence Entity-Views 的示例 DTO 模型可能如下所示:

    @EntityView(User.class)
    public interface UserDto {
        @IdMapping
        Long getId();
        String getName();
        Set<RoleDto> getRoles();
    
        @EntityView(Role.class)
        interface RoleDto {
            @IdMapping
            Long getId();
            String getName();
        }
    }
    

    查询是将实体视图应用于查询的问题,最简单的就是通过 id 进行查询。

    UserDto a = entityViewManager.find(entityManager, UserDto.class, id);

    Spring Data 集成让您可以像使用 Spring Data Projections 一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

    即使 UserRole 实体包含更多列,通过使用 Blaze-Persistence Entity-Views 也只会获取实际需要的数据。

    Blaze-Persistence Entity-Views 具有与 Hibernate 类似的获取策略,并且还有另一个称为 MULTISET 的策略,这是所有策略中最好的。它将利用 DBMS 端的 JSON 或字符串聚合函数将连接扁平化为单个列。这避免了 N * M 结果集爆炸,同时仍然在单个查询中执行。这里是关于获取策略的文档:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/#anchor-fetch-strategies

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-17
      • 2012-09-06
      • 1970-01-01
      • 1970-01-01
      • 2011-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多