У меня есть веб-приложение, которое получает сообщения через 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 кэшируется между каждой транзакцией.