我试图提高应用程序的性能,所以我们做的第一件事就是查看数据库查询以优化它们。
我们所做的一件事是在适当的地方添加“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似乎需要更多的内存来实现这一点。
在分析应用程序时,我可以看到每秒分配的对象数超过了500000。垃圾收集器跟不上,应用程序冻结,最终导致内存不足。
从hprof来看,大多数对象似乎是在hibernate类中分配的。最严重的罪犯似乎是大部分人 select where id in (?1)
. 它每秒执行一次,检索大约500个对象,但似乎负责每秒大约30万个对象的分配。
我的猜测是,由于连接,数据库返回300000多行(这确实是我手动运行时得到的结果) select count(*) from stuff join other_stuff
),hibernate必须在整合最后500个对象之前进行分配。
我说得对吗?
我想我不会做的 left join fetch
让hibernate用一个单独的查询来加载它们会大大减少这种分配,但会影响性能。
有没有一种方法可以减少内存分配,同时仍然可以立即获取数据?
谢谢
1条答案
按热度按时间kiz8lqtg1#
在hibernate中有3种不同的获取策略可供使用,不过在查询中只能指定连接获取。其他的只是注解,并在延迟加载期间生效。
JOIN
获取sql连接,这可能会导致n*m结果集爆炸,如您所想SUBSELECT
通过嵌入主查询为关联执行一个查询SELECT
(默认)为主查询中的每n个元素发出一个select by id查询,其中n是配置的批大小。如果主查询非常复杂,因此SUBSELECT
不合适。建议将批处理大小与通常在主查询中预期的元素数量对齐,以避免执行太多查询。通常,我建议
SUBSELECT
获取,除非查询花费的时间太长或者可以估计主查询生成的元素数量。其他性能改进可以通过减少select项的数量来实现,但这需要创建自定义dto模型。
我认为这是blaze持久性实体视图的完美用例。
我创建了这个库,以便在jpa模型和自定义接口或抽象类定义的模型之间进行简单的Map,比如spring数据在steroids上的投影。其思想是以您喜欢的方式定义目标结构(域模型),并通过jpql表达式将属性(getter)Map到实体模型。
对于blaze持久性实体视图,示例dto模型可以如下所示:
查询是将实体视图应用于查询的问题,最简单的就是按id进行查询。
UserDto a = entityViewManager.find(entityManager, UserDto.class, id);
spring数据集成允许您像spring数据投影一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_us/index.html#spring-数据特征即使
User
或者Role
实体包含更多的列,通过使用blaze持久性实体视图,只有实际需要的数据才会被获取。blaze持久性实体视图具有类似于hibernate的获取策略,还有另一个称为
MULTISET
,这是其中最好的。它将利用dbms端的json或字符串聚合函数将连接展平到单个列中。这避免了n*m结果集的爆炸,同时仍然在单个查询中执行。以下是有关获取策略的文档:https://persistence.blazebit.com/documentation/entity-view/manual/en_us/#anchor-获取策略