Вопрос

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

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

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

Это сообщение должно быть обработано следующим образом:

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

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

Я использую 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 кэшируется между каждой транзакцией.

Это было полезно?

Решение

Вам следует отсоединиться от интерфейса 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, на самом деле тратится на чтение информации о секвенировании из базы данных, особенно с небольшими значениями по умолчанию (я, вероятно, имел бы размер последовательности несколько сотен или даже больше).

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top