Есть ли способ с помощью Spring Boot и Hibernate пакетной вставки сущностей с первичными ключами UUID, используя postgres?

#java #spring #postgresql #hibernate #jpa

Вопрос:

Так что я сделал свою домашнюю работу, насколько мне известно. Я знаю, что автоматически сгенерированные ключи не могут быть вставлены. Но как мне обойти это для первичных ключей, которые являются UUID?

У меня есть эта конфигурация первичного ключа

 @Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid2")
private UUID id;
 

Я надеялся, что это не считалось «АВТОМАТИЧЕСКОЙ» стратегией, но, похоже, так оно и есть, потому что мои объекты все еще вставляются по отдельности, что снижает мою производительность с помощью 10 тыс. вставок. Почему это нельзя считать стратегией последовательности? UUID в любом случае гарантированно уникальны, поэтому не должно быть ничего технического в том, чтобы просто генерировать несколько UUID и вставлять их в пакет.

У меня есть соответствующие настройки в моем приложении.свойства:

 spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
spring.jpa.properties.hibernate.jdbc.batch_versioned_data=true
 

И я действительно использую PagingAndSortingRepository метод saveAll a для сохранения сущностей.

Несколько бревен:

 03:12:01.122 |  INFO | o.h.e.i.StatisticalLoggingSessionEventListener | Session Metrics {
    35000 nanoseconds spent acquiring 1 JDBC connections;
    0 nanoseconds spent releasing 0 JDBC connections;
    1533100 nanoseconds spent preparing 8 JDBC statements;
    49660800 nanoseconds spent executing 5 JDBC statements;
    5137013700 nanoseconds spent executing 401 JDBC batches;
    0 nanoseconds spent performing 0 L2C puts;
    0 nanoseconds spent performing 0 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;
    6933591600 nanoseconds spent executing 2 flushes (flushing a total of 60152 entities and 40294 collections);
    244354300 nanoseconds spent executing 5 partial-flushes (flushing a total of 60009 entities and 60009 collections)
}
 

Что интересно, потому что в нем упоминаются партии. Однако при spring.jpa.show-sql=true включении я вижу несколько тысяч строк журнала, таких как следующие:

Hibernate: insert into ingestion_id_mapping (derive_ingestion_id, ingestion_id, project_id, metric_id, subject_id) values (?, ?, ?, ?, ?)

Что, похоже, не коррелирует с числом в статистике дозирования.

Комментарии:

1. Проблема пакетной обработки связана не с АВТОМАТИЧЕСКОЙ стратегией, а с ИДЕНТИФИКАЦИЕЙ. АВТО в этом случае следует использовать генератор UUID. У вас есть какие-нибудь журналы ? какую версию PG и hibernate вы используете ?

2. @CodeScale Я использую PostgreSQL 13 (в любом случае локально) и Hibernate, который поставляется с Spring Boot 2.5.0, в основном с истекающим краем. Я еще немного поэкспериментировал с другой сущностью, у которой есть @ EmbeddedId в виде 2 внешних ключей UUID, но затем, еще до вставки, он начал извлекать отдельные строки по этим идентификаторам UUID. Именно тогда я решил сдаться и просто использовать SQL INSERT INTO. Слишком много магии, которую я не могу отладить.

3. У вас есть какие-нибудь журналы ? Как и тот, который генерируется spring.jpa.properties.hibernate.generate_statistics=true собственностью

4. @CodeScale Я запустил его снова с включенным ведением журнала. Однако результаты сбивают с толку.

Ответ №1:

Повторяя ваш пример следующим образом, он на самом деле работает так, как ожидалось, при условии, что reWriteBatchedInserts установлено свойство подключения jdbc:

 spring.datasource.url=jdbc:postgresql://localhost:5432/postgres?reWriteBatchedInserts=true
 

Используя, например, с spring.jpa.properties.hibernate.jdbc.batch_size=8 , соответствующий журнал postgres будет выглядеть следующим образом при вставке 8 вновь созданных объектов через repository.saveAll :

 postgres_db_1  | 2021-06-02 15:22:54.327 UTC [386] LOG:  
  execute <unnamed>: /* insert org.demo.batchinsert.UuidEntity */
  insert into uuid_entity (id) values ($1),($2),($3),($4),($5),($6),($7),($8)
...
 

Комментарии:

1. Инструкции Insert, по-видимому, группируются кратно 2, поэтому, когда требуется, например, 7 вставок, выполняемые вставки будут вставлять 4, 2 и 1 значение(значения) одновременно (что может показаться запутанным при просмотре журналов бд)

2. Что ж, в любом случае это интересный вариант postgres, спасибо. К сожалению, как это часто бывает, простые примеры не показывают проблему. Возможно, причиной этого является сочетание различных объектов в моем проекте.

3. reWriteBatchedInserts предназначен для ускорения производительности на стороне базы данных, но это актуально только в том случае, если клиент отправляет инструкции в пакетном режиме… если нет, это вообще не имеет никакого эффекта….

4. @себастьян, добро пожаловать. из ваших журналов гибернации похоже, что инструкции для отдельных вставок отправляются в БД пакетами, где бд затем выполняет отдельные инструкции. изменила ли опция reWriteBatchedInserts запросы?

Ответ №2:

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

Вы должны использовать прокси JDBC Driver -сервер или Datasource с помощью такого инструмента, как P6Spy. Тогда вы, вероятно, увидите, что пакетирование действительно работает.

Вы можете использовать этот декоратор для этого : https://github.com/gavlyukovskiy/spring-boot-data-source-decorator.

Теперь, если вы хотите ускорить пакетную обработку и на стороне базы данных, это свойство reWriteBatchedInserts может быть использовано.