【问题标题】:Bulk/Batch update using Spring Data JPA/Hibernate on Mysql在 Mysql 上使用 Spring Data JPA/Hibernate 进行批量/批量更新
【发布时间】:2018-10-28 08:35:02
【问题描述】:

我正在使用 Mysql,Spring Data JPA。在我的用例中,我只有 1 个表,例如客户(ID、FIRST_NAME、LAST_NAME) 我想要实现的是批量/批量更新,其中更新语句是一个组,如上面示例中所示以减少数据库往返。

我已经设置了所有属性

  • hibernate.order_inserts: true
  • hibernate.order_updates: true
  • hibernate.jdbc.batch_versioned_data: true

但结果是(更新语句未分组):来自 MySQL 常规日志的日志

2018-10-28T03:18:32.545233Z 1711 Query update CUSTOMER set FIRST_NAME=’499997′, LAST_NAME=’499998′ where id=499996;
2018-10-28T03:18:32.545488Z 1711 Query update CUSTOMER set FIRST_NAME=’499998′, LAST_NAME=’499999′ where id=499997;
2018-10-28T03:18:32.545809Z 1711 Query update CUSTOMER set FIRST_NAME=’499999′, LAST_NAME=’500000′ where id=499998;

期望的结果:(更新被分组为单个查询,从而减少了数据库往返)

2018-10-28T03:18:32.545233Z 1711 Query update CUSTOMER set FIRST_NAME=’499997′, LAST_NAME=’499998′ where id=499996; update CUSTOMER set FIRST_NAME=’499998′, LAST_NAME=’499999′ where id=499997; update CUSTOMER set FIRST_NAME=’499999′, LAST_NAME=’500000′ where id=499998;

我的应用程序需要执行超过 1 亿次更新,我想这可能是最快的方式。

【问题讨论】:

  • 我相信 this 是您可以使用 Hibernate/JPA 获得的最接近的结果

标签: performance hibernate spring-data-jpa batch-updates batching


【解决方案1】:

我建议你也设置hibernate.jdbc.batch_size 属性。以下是我尝试过的一个小例子:

int entityCount = 50;
int batchSize = 25;

EntityManager entityManager = entityManagerFactory()
    .createEntityManager();

EntityTransaction entityTransaction = entityManager
    .getTransaction();

try {
    entityTransaction.begin();

    for (int i = 0; i < entityCount; i++) {
        if (i > 0 && i % batchSize == 0) {
            entityTransaction.commit();
            entityTransaction.begin();

            entityManager.clear();
        }

        Post post = new Post(
            String.format("Post %d", i + 1)
        );

        entityManager.persist(post);
    } 

    entityTransaction.commit();
} catch (RuntimeException e) {
    if (entityTransaction.isActive()) {
        entityTransaction.rollback();
    }
    throw e;
} finally {
    entityManager.close();
}

每次迭代计数器(例如 i)达到 batchSize 阈值的倍数时,我们可以刷新 EntityManager 并提交数据库事务。通过在每次批处理执行后提交数据库事务,我们获得了以下优势:

  • 我们避免了对 MVCC 关系数据库系统有害的长时间运行的事务。
  • 我们确保如果出现故障,我们不会丢失之前成功执行的批处理作业所做的工作。

EntityManager 在每次批处理执行后都会被清除,这样我们就不会继续累积可能导致几个问题的托管实体:

  • 如果要持久化的实体数量巨大,我们可能会耗尽内存。
  • 我们在持久性上下文中积累的实体越多,刷新就越慢。因此,确保 Persistence Context 尽可能精简是一种很好的做法。

如果抛出异常,我们必须确保回滚当前正在运行的数据库事务。如果不这样做,可能会导致许多问题,因为数据库可能仍然认为事务是打开的,并且可能会持有锁,直到事务因超时或 DBA 结束。

最后,我们需要关闭 EntityManager,以便我们可以清除上下文并释放 Session 级资源。

【讨论】:

    猜你喜欢
    • 2013-07-11
    • 2019-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-21
    • 1970-01-01
    • 1970-01-01
    • 2020-03-18
    相关资源
    最近更新 更多