Пакетная вставка с использованием JPA / Toplink

У меня есть веб-приложение, которое получает сообщения через HTTP-интерфейс, например:

http://server/application?source=123&destination=234&text=hello

Этот запрос содержит идентификатор отправителя, идентификатор получателя и текст сообщения.

Это сообщение следует обрабатывать так:

  • поиск соответствующего объекта User для источника и назначения из базы данных
  • создание дерева объектов: Сообщение, которое содержит поле для текста сообщения и два объекта User для источника и назначения
  • сохранение этого дерева в базе данных.

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

Я использую Oracle в качестве резервной базы данных и JPA с Toplink для задач обработки базы данных. Если возможно, я останусь с ними.

Без особой оптимизации я могу достичь пропускной способности ~ 30 запросов / сек в своей среде. Это немного, мне нужно ~ 300 запросов / сек. Итак, я измерил, где находится узкое место в производительности, и обнаружил, что вызовы em.persist() занимают большую часть времени. Если я просто закомментирую эту строку, пропускная способность превысит 1000 запросов / сек.

Я попытался написать небольшое тестовое приложение, которое использовало бы простые вызовы JDBC для сохранения 1 миллиона сообщений в той же базе данных. Я использовал пакетную обработку, то есть я сделал 100 вставок, затем зафиксировал и повторял, пока все записи не были в базе данных. Я измерил пропускную способность ~ 500 запросов / сек в этом сценарии, что соответствовало моим потребностям.

Понятно, что здесь мне нужно оптимизировать производительность вставки. Однако, как я упоминал ранее, я хотел бы продолжать использовать для этого JPA и Toplink, а не чистый JDBC.

Вы знаете, как создавать пакетные вставки с помощью JPA и Toplink? Можете ли вы порекомендовать какой-либо другой метод улучшения производительности JPA-файлов?

ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ:

«запросов / сек» здесь означает: общее количество запросов / общее время от начала теста до последней записи, записанной в базу данных.

Я попытался сделать вызовы em.persist() асинхронными, создав очередь в памяти между содержимым сервлета и персистером. Это очень помогло производительности. Однако очередь действительно росла очень быстро, и поскольку приложение будет получать ~ 200 запросов в секунду непрерывно, это не приемлемое решение для меня.

В этом независимом подходе я собирал запросы в течение 100 мс и вызывал em.persist() для всех собранных элементов перед тем, как совершить транзакцию. EntityManagerFactory кэшируется между каждой транзакцией.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
0
6 698
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Каков ваш показатель «запросов / сек»? Другими словами, что происходит с 31-м запросом? Какой ресурс блокируется? Если это интерфейс / сервлет / веб-часть, можете ли вы запустить em.persist () в другом потоке и немедленно вернуться?

Кроме того, вы каждый раз создаете транзакции? Вы создаете объекты EntityManagerFactory с каждым запросом?

Ответ принят как подходящий

Вы должны отделиться от интерфейса JPA и использовать чистый API TopLink. Вероятно, вы можете поместить сохраняемые объекты в UnitOfWork и зафиксировать UnitOfWork по своему расписанию (синхронно или асинхронно). Обратите внимание, что одна из издержек em.persist () - это неявное клонирование всего графа объекта. TopLink будет работать лучше, если вы сами uow.registerObject () ваши два пользовательских объекта сохраните сами тесты идентичности, которые он должен выполнить в противном случае. В итоге вы получите:

uow=sess.acquireUnitOfWork();
for (job in batch) {
 thingyCl=uow.registerObject(new Thingy());
 user1Cl=uow.registerObject(user1);
 user2Cl=uow.registerObject(user2);
 thingyCl.setUsers(user1Cl,user2Cl);
}
uow.commit();

Кстати, это очень старая школа TopLink;)

Обратите внимание, что пакетная обработка очень поможет, потому что пакетная запись и, особенно, пакетная запись с привязкой параметров, будет работать, что в этом простом примере, вероятно, будет иметь очень большое влияние на вашу производительность.

Другие вещи, на которые стоит обратить внимание: размер секвенирования. Большая часть времени, затрачиваемого на запись объектов в TopLink, на самом деле тратится на чтение информации о последовательности из базы данных, особенно с небольшими значениями по умолчанию (у меня, вероятно, было бы несколько сотен или даже больше в качестве размера моей последовательности).

Спасибо, попробую через несколько дней. Что вы имеете в виду под размером секвенирования?

Zizzencs 16.09.2008 01:47

Другие вопросы по теме