批量插入实体到数据库(Quarkus,Hibernate)
Batch insert entities to DB (Quarkus, Hibernate)
首先:我不习惯 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 时,我得到了这个 varning:
Cannot resolve configuration item 'statement-batch-size'
我花了将近一天的时间试图找到有关如何加快速度的解决方案,我在这里遗漏了什么明显的问题吗?
And/or:
我可以用某种神奇的方式包装从 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();
很难明确回答这个问题,除非您已经确定响应时间不佳的确切原因。原则上可能是由于:
- 与对数据库服务器的许多请求相关的延迟,
- 在 Hibernate 有状态会话中将许多实体对象固定在内存中的开销,甚至
- 接收和解析传入数据的成本。
我们假设它不是 3。
- 如果它是 2,那么 JDBC 批处理确实会有所帮助,您只需要弄清楚如何使该配置 属性 起作用。
- 但我的猜测是克里斯蒂安是正确的,问题是持久化上下文中的数据积累。如果这个猜测是正确的,那么有两种可能的解决方案:一种是使用
StatelessSession
,它是为这种用法而设计的,另一种是使用flush()
和clear()
] 正如克里斯蒂安所描述的那样。
我建议使用StatelessSession
,除非问题实际上是2+3的组合,在这种情况下你需要两者 批处理, 和 持久性上下文管理,然后在这种情况下执行 Christian 的建议。
首先:我不习惯 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 时,我得到了这个 varning:
Cannot resolve configuration item 'statement-batch-size'
我花了将近一天的时间试图找到有关如何加快速度的解决方案,我在这里遗漏了什么明显的问题吗?
And/or:
我可以用某种神奇的方式包装从 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();
很难明确回答这个问题,除非您已经确定响应时间不佳的确切原因。原则上可能是由于:
- 与对数据库服务器的许多请求相关的延迟,
- 在 Hibernate 有状态会话中将许多实体对象固定在内存中的开销,甚至
- 接收和解析传入数据的成本。
我们假设它不是 3。
- 如果它是 2,那么 JDBC 批处理确实会有所帮助,您只需要弄清楚如何使该配置 属性 起作用。
- 但我的猜测是克里斯蒂安是正确的,问题是持久化上下文中的数据积累。如果这个猜测是正确的,那么有两种可能的解决方案:一种是使用
StatelessSession
,它是为这种用法而设计的,另一种是使用flush()
和clear()
] 正如克里斯蒂安所描述的那样。
我建议使用StatelessSession
,除非问题实际上是2+3的组合,在这种情况下你需要两者 批处理, 和 持久性上下文管理,然后在这种情况下执行 Christian 的建议。