在MySQL上使用Spring Data JPA / Hibernate批量/批量更新

时间:2018-10-28 08:35:02

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

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

我已经设置了所有属性

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

但是结果是(更新语句未分组): 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亿次更新,我想这可能是最快的方法。

1 个答案:

答案 0 :(得分:-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,以免我们继续积累会导致多个问题的托管实体:

  • 如果要保留的实体数量巨大,则存在内存不足的风险。
  • 我们在持久性上下文中积累的实体越多,刷新变得越慢。因此,最好的方法是确保持久性上下文尽可能小。

如果引发异常,则必须确保回滚当前正在运行的数据库事务。否则,可能会导致许多问题,因为数据库可能仍然认为事务已打开并且锁可能会一直保留,直到事务因超时或DBA而终止。

最后,我们需要关闭EntityManager以便清除上下文并取消分配会话级资源。