Пакетная вставка с использованием JPA / Toplink - PullRequest
2 голосов
/ 15 сентября 2008

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

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

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

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

  • поиск подходящего объекта 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 кэшируется между каждой транзакцией.

Ответы [ 2 ]

3 голосов
/ 16 сентября 2008

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

0 голосов
/ 15 сентября 2008

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

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

...