【问题标题】:Batch insert entities to DB (Quarkus, Hibernate)批量插入实体到数据库(Quarkus、Hibernate)
【发布时间】:2021-08-09 16:00:37
【问题描述】:

首先:我不习惯 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:

无法解析配置项'statement-batch-size'

我花了将近一天的时间试图找到如何加快速度的解决方案,我在这里错过了什么明显的东西?

和/或:

我可以将来自service 的调用封装在某种神奇的火焰中并忘记 Quarkus 或 Vert.x 中内置的调用吗?

【问题讨论】:

  • 也许你可以试试 Hibernate Reactive:hibernate.org/reactive。还有一个关于在 Quarkus 中使用它的快速入门:github.com/quarkusio/quarkus-quickstarts/tree/main/…
  • 我很快就调查过了,但据我了解,这还不支持 H2 数据库吗?我将使用 mariadb 进行尝试,但我们决定使用 H2 作为标准,并在需要时覆盖服务器。编辑:在尝试这个时,我一直在使用 MariaDB,而不是 H2。

标签: java hibernate vert.x quarkus


【解决方案1】:

除非您确定了响应时间不佳的确切原因,否则很难明确回答这个问题。原则上可能是由于:

  1. 与对数据库服务器的许多请求相关的延迟,
  2. 在 Hibernate 有状态会话中将许多实体对象固定在内存中的开销,甚至
  3. 接收和解析传入数据的成本。

让我们假设它不是 3。

  • 如果是 2,那么 JDBC 批处理确实会有所帮助,您只需要弄清楚如何使该配置属性起作用。
  • 但我的猜测是Christian是正确的,问题在于持久化上下文中的数据积累。如果这个猜测是正确的,那么有两种可能的解决方案:一种是使用StatelessSession,它是根据这种用法设计的,另一种是使用flush()clear(),如 Christian 所述。

我会推荐使用StatelessSession除非问题实际上是 2+3 的组合,在这种情况下你需要 both 批处理,and 持久化上下文管理,然后在这种情况下执行 Christian 的建议。

【讨论】:

  • 谢谢!是的,我知道在没有所有事实和数据的情况下很难指出问题所在。
【解决方案2】:

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

【讨论】:

  • 这确实加快了速度,所以我将其标记为正确。但奇怪的是我不能让物业工作。谢谢!
  • 该属性是一个额外的配置,也可以在 JDBC 级别上进行批处理。通常最好调整您使用的批次大小以获得最佳性能。
猜你喜欢
  • 2011-02-15
  • 1970-01-01
  • 2012-02-16
  • 2011-01-14
  • 1970-01-01
  • 2015-07-03
  • 2011-07-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多