2010-06-23 9 views
4

Я пытаюсь выполнить каскадное сохранение на крупном объектном графе с использованием JPA. Например (мой график объект немного больше, но достаточно близко):JPA вставляет медленно с графом объектов

@Entity 
@Table(name="a") 
public class A { 
    private long id; 
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "a") 
    private Collection<B> bs; 
} 

@Entity 
@Table(name="b") 
public class B { 
    private long id; 
    @ManyToOne 
    private A a; 
} 

Так что я пытаюсь упорствовать А, который имеет коллекцию 100+ Б. Код только

em.persist(a); 

Проблема в том, что она МЕДЛЕННАЯ. Мое сохранение занимает приблизительно 1300 мс. Я посмотрел на генерируемый SQL и ужасно неэффективен. Что-то вроде этого:

select a_seq.nextval from dual; 
select b_seq.nextval from dual; 
select b_seq.nextval from dual; 
select b_seq.nextval from dual; 
... 
insert into a (id) values (1); 
insert into b (id, fk) values (1, 1); 
insert into b (id, fk) values (2, 1); 
insert into b (id, fk) values (3, 1); 
... 

В настоящее время используется TopLink в качестве поставщика сохраняемости, но я пытался EclipseLink и зимуют также. Бэкэнд - оракул 11g. Проблема в том, как скомпилирован sql. Каждая из этих операций выполняется дискретно, а не навалом, поэтому, если есть сетевая латентность даже 5 мс между моим сервером приложений и сервером db, выполнение 200 дискретных операций добавляет 1 секунду. Я попытался увеличить выделение в моих последовательностях, но это немного помогает. Я также попытался прямым JDBC в качестве пакетного высказывания:

for...{ 
    statement = connection.prepareStatement(sql); 
    statement.addBatch(); 
} 
statement.executeBatch(); 

Для моей DataModel это занимает около 33ms сделано как прямой JDBC партии. Сам Oracle принимает 5 мс для 100 + вставок.

Есть ли способ сделать JPA (я застрял с 1.0 прямо сейчас ...) идти быстрее, не вникая в конкретные вещи поставщика, такие как вставка для спящего режима?

Спасибо!

ответ

2

Решение было бы включить JDBC пакетирование и промыть и очистить EntityManager через регулярные промежутки времени (то же самое, чем размер партии), но я не знаю о поставщика нейтрального способа сделать это:

  • С Hibernate вам необходимо установить опцию конфигурации hibernate.jdbc.batch_size. См. Chapter 13. Batch processing

  • С EclipseLink это похоже на режим пакетной записи. См. Сообщение Джеффа Сазерленда в this thread (также следует указать размер).

  • Согласно комментариям this blog post, партия письмо не доступен в TopLink Сути :(

+0

благодарит за отзыв! Будет опубликовать то, что я сделал ниже! – user364939

+0

Спасибо за информацию, очень приятно – Greg

1

Спасибо Pascal для ответа. Я сделал несколько тестов, и я был в состоянии значительно увеличить производительность .

с без оптимизации я имел вкладыш с приблизительно 1100ms Использование EclipseLink я добавил к persistence.xml:.

<property name="eclipselink.jdbc.batch-writing" value="JDBC"/> 
    <property name="eclipselink.jdbc.batch-writing.size" value="1000"/> 

Я попробовал другие свойства (Oracle-JDBC и т. Д.), Но JDBC, похоже, дал лучшее увеличение производительности. Это привело к тому, что вставка упала примерно до 900 мс. Таким образом, довольно скромное увеличение производительности на 200 мс. Большая сэкономленность пришла от увеличения распределения sequenceSize. Я не большой поклонник этого. Я считаю грязным увеличить INCREMENT BY моих последовательностей только для размещения JPA. Увеличение их привело к сокращению времени до примерно 600 мс для каждой вставки. Таким образом, в общей сложности около 500 мс были сбриты с этими улучшениями.

Все это прекрасно и денди, но оно все еще значительно медленнее, чем партия JDBC. JPA - довольно высокая цена, чтобы платить за простоту кодирования.

+0

Спасибо за отзыв. Я должен был заметить «allocSize». +1 –

2

Любопытно, почему вы находите увеличение INCREMENT BY таким же грязным? Это оптимизация, которая уменьшает количество вызовов в базе данных для получения следующего значения последовательности и является общим шаблоном, используемым в клиентах базы данных, где значение id назначено клиенту до INSERT. Я не рассматриваю это как проблему JPA или ORM и должен иметь одинаковую стоимость в сравнении JDBC, так как он также должен получить новый порядковый номер для каждой новой строки до INSERT. Если у вас другой подход в вашем случае JDBC, мы должны иметь возможность заставить EclipseLink JPA следовать тому же подходу.

Стоимость JPA, вероятно, наиболее очевидна в изолированном сценарии INSERT, потому что вы не получаете никакой пользы от повторных чтений в транзакционном или общем кэше, и в зависимости от конфигурации вашего кеша вы платите цену, чтобы поместить эти новые сущности в кеш внутри флеша/фиксации.

Обратите внимание, что также существует стоимость создания первого EntityManager, где выполняются все обработки метаданных, загрузка классов, возможно плетение и метамодель. Убедитесь, что вы держите это время вне вашего сравнения. В вашем реальном приложении это происходит один раз, и все последующие EntityManager извлекают выгоду из общих метаданных.

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

Надеюсь, это поможет. С удовольствием предоставляем любые рекомендации и EclipseLink JPA, а также параметры производительности и масштабируемости.

Doug

+0

Спасибо за ответ. Для выбора последовательности с такими базами данных, как oracle, я не уверен, почему вы не можете просто вставить это в инструкцию insert (my_seq.nextval). Это латентность сети делает это много раз, что приводит к замедлению. Время, затрачиваемое на захват следующего значения последовательности в оракуле, статистически незначимо. – user364939

+2

Присвоение значения в инструкции INSERT в большинстве баз данных отличается более высокой скоростью. Задача состоит в том, что вам также требуется новое значение в приложении для кэширования, для поддержания идентичности или для заполнения каскадных первичных ключей. Если ваша база данных поддерживает использование nextval в INSERT, она также должна вернуть значение из INSERT для использования поставщиком JPA. –

+0

хорошо пункт ..... – user364939