ScrollableResultSet.next() постепенно замедляется при использовании Session.save() во время прокрутки

Я использую ScrollableResults Объект для прокрутки от 500 000 до 1 000 000 строк из таблицы. Во время прокрутки я создаю отдельную сущность, используя полученную сущность из каждой итерации, и использую session.save() сохранить этот объект. Ниже приведен пример кода, где реальный код более сложен, но по сути делает то же самое.

Session = getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
ScrollableResults results = session.createQuery("from Foo_Table f join f.bars b")
    .scroll(ScrollMode.FORWARD_ONLY);
int i = 0;
while(results.next())
{
    Foo foo = (Foo) results.get(0);
    Bar bar = new Baz(foo);
    bar.setFoo(foo);

    session.save(bar)

    if(i % 50 == 0)
    {
        session.flush();
        session.clear();
    }
}

tx.commit();
session.close();

Важные объекты:

@Entity
@Table(name = "FOO_TABLE")
public class Foo_Entity implements Serializable {
    @Id    
    @Column(name = "Foo_ID", nullable=false)
    private String id;

    @OneToMany(fetch = FetchType.EAGER, //FetchType.LAZY fixes the slow down 
               mappedBy = "fooParent", cascade = CascadeType.ALL)
    private Set<Bar> bar_entities = new HashSet<>(0);
}

@Entity
@Table(name = "BAR_TABLE")
public class Bar_Entity implements Serializable {
    @Id
    @GeneratedValue
    @Column(name="Id")
    private Long id;

    @ManyToOne
    @JoinColumn(name="foo_pk")
    private Foo fooParent;

    // setFoo, getFoo...

}

Когда я определяю время этой транзакции, время запуска начинается примерно с 100 мс на 500 итераций, но постепенно увеличивается до нескольких секунд на 500 итераций после примерно 20 000 итераций. В результате транзакция имеет крайне низкую производительность. Единственная строка кода, которая занимает какое-то время, это results.next(), который постепенно занимает все больше и больше времени для выполнения.

Проблема решается, если я изменю тип выборки для сущностей Bar в Foo с нетерпеливого на ленивый. Я не понимаю, почему использование типа нетерпеливого извлечения для набора, который еще не заполнен, вызывает проблемы с прокруткой объектов, которые содержат отношения. Набор действительно заполняется во время прокрутки в session.flush(), но в моем сценарии набор обычно заполняется только одним-двумя элементами, поэтому я бы предпочел, чтобы этот тип выборки был активным.

Кто-нибудь знает, почему это замедление происходит для этого конкретного сценария?

Обратите внимание, что этот вопрос был впервые опубликован до того, как я понял, что изменение типа выборки решило проблему, поэтому вопрос теперь сместился с "Как я могу это исправить" на "Почему это проблема?"

2 ответа

Решение

Отсутствие индекса в столбце BAR_TABLE.foo_pk приведет к замедлению процесса с активной загрузкой, поскольку будет выполнено полное сканирование таблицы для загрузки объекта BAR, связанного с каждым объектом FOO,

Во-первых, если fetch стремится, что означает, что отложенная загрузка ложна, то Bar_Entity загружаются всякий раз, когда загружаются Foo_Entity. Так что или удалите объединение в запросе или сделайте выборку ленивой. Наличие обоих является излишним.

Во-вторых, что касается замедления. Так как вы открываете сеанс с состоянием. каждый объект кэшируется в памяти благодаря кибернетическому кешу первого уровня. В этом сценарии замедление не имеет ничего общего с ленивым или нетерпеливым или присоединиться. Замедление происходит из-за количества объектов, хранящихся в кеше (памяти) в режиме гибернации. Попробуйте использовать сеанс без сохранения состояния. Тогда замедление должно уйти. Пожалуйста, обратитесь к ниже URL

https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/batch.html

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