Массовое / пакетное обновление с использованием Spring Data JPA / Hibernate на Mysql - PullRequest
0 голосов
/ 28 октября 2018

Я использую Mysql, Spring Data JPA.В моем случае использования у меня есть только 1 таблица, например, Customer (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;

Мое приложение должно выполнить более 100 миллионов обновлений, и я полагаю, что это может быть самым быстрым способом.

1 Ответ

0 голосов
/ 28 октября 2018

Я предложу вам также настроить свойство 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 очищается после каждого пакетного выполнения, чтобы мы не продолжали накапливатьуправляемые объекты, которые могут вызывать несколько проблем:

  • Если количество объектов, которые необходимо сохранить, огромно, мы рискуем исчерпать память.
  • Чем больше объектов мы накапливаем в постоянствеКонтекст, тем медленнее становится флеш.Поэтому рекомендуется сделать как можно более тонкий контекст сохраняемости.

Если выдается исключение, мы должны обязательно откатить текущую текущую транзакцию базы данных.Невыполнение этого требования может вызвать много проблем, так как база данных может все еще думать, что транзакция открыта, и блокировки могут удерживаться до тех пор, пока транзакция не будет завершена по таймауту или администратором базы данных.

В конце нам нужнозакрыть EntityManager, чтобы мы могли очистить контекст и освободить ресурсы уровня сеанса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...