#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
может быть использовано.