首先:我不习惯 Quarkus 或 Hibernate(我几乎都是 .net)
问题:
我的服务收到约 10k 的列表(我猜这是最常见的数字)。 这是通过资源端点实现的,需要 10 秒以上才能完成,时间太长了。并且服务没有响应。
*Endpoint -> Service/Business -> DAO*
@Override
public void create(FooBusiness foo) {
var statuses = new ArrayList<StatusDto>();
for(var i = 1; i < foo.getFromList().size(); i++){
var bar = foo.getFromList().get(i);
statuses.add(new StatusDto(bar.x, bar.y));
}
statusDao.create(statuses);
}
statusDao.Create() 注释为
@Transactional
:
DAO 是
@ApplicationScoped
这个 EM 是:
@PersistenceContext
EntityManager entityManager;
statusDao.Create():
@Transactional
public List<StatusDto> create(List<StatusDto> dto) {
for(var i = 0; i < dto.size(); i++){
var status = dto.get(i);
status.setCreatedTimestamp(LocalDateTime.now());
entityManager.persist(status);
}
entityManager.flush();
return dto;
}
我已经阅读了很多关于此的文章,其中许多人建议使用此属性,并将持久循环拆分为与批处理大小相同: quarkus.hibernate-orm.jdbc.statement-batch-size
问题是,当我将其添加到 application.properties 时,我得到这个警告:
无法解析配置项“statement-batch-size”
我花了几乎一天的时间试图找到如何加快速度的解决方案,有什么明显的我在这里错过的吗?
和/或:
我可以将从
service
到 dao
的调用包装在 Quarkus 或 Vert.x 中内置的某种神奇的火并忘记调用中吗?
Hibernate 将您持久化的所有实体保留在持久化上下文中,因此您将获得越来越多的内存,这可能会导致性能不佳。如果您不再需要这些实体,您可以在例如中刷新并清除它们。批量 50 件。
for (var i = 0; i < dto.size();) {
var status = dto.get(i);
status.setCreatedTimestamp(LocalDateTime.now());
entityManager.persist(status);
i++;
if ((i % 50) == 0) {
entityManager.flush();
entityManager.clear();
}
}
entityManager.flush();
除非您已经确定了响应时间差的确切原因,否则很难明确回答这个问题。原则上可能是由于:
我们假设不是 3。
StatelessSession
,它是为这种用法而设计的,另一种是使用 Christian 描述的 flush()
和 clear()
。 我建议使用
StatelessSession
,除非问题实际上是2+3的组合,在这种情况下你需要两者批处理,和持久上下文管理,然后在这种情况下按照Christian的建议去做。
您可以将
quarkus.hibernate-orm.jdbc.statement-batch-size
配置设置为 > 0(建议值在 5 到 30 之间)。这使得 Hibernate 能够进行 JDBC2 批量更新。请参阅this博客文章了解详细信息和基准。