Hibernate (PostgreSQL) предложение медленного выбора WHERE по внешнему ключу по сравнению с jdbc - PullRequest
0 голосов
/ 03 января 2019

Я написал приложение, которое очищает плейлисты интернет-радио, а затем сохраняет их в базе данных.Чтобы узнать о спящем режиме, я перенес приложение в режим гибернации, но при поиске в SELECT ... WHERE наблюдался большой спад производительности по сравнению с другими попытками.Та же самая процедура (чтобы получить около 17 000 треков, сгруппированных по какой программе они исполнялись и кто их играл) заняла 150 мс в моем прототипе python sqlite, а первоначальная java-версия с использованием утилиты apache db, которая заняла около 250 мс, по сравнению с моей (вероятно, ужасающей)) спящий вариант, который занимает около 1100 мс.

@Override
public DJAllProgrammes getAllProgrammesFromDJ(Collection<String> names) {
    DJAllProgrammes djAllProgrammes = new DJAllProgrammes();
    session.beginTransaction();
    List<Presenter> result = session.createQuery("from Presenter p WHERE p.presenter_name in :names", Presenter.class)
            .setParameterList("names", names)
            .getResultList();
    for (Presenter presenter : result) {
        int presenter_id = presenter.getPresenter_id();
        List<Programme> programmes = session
                .createQuery("from programme prog WHERE prog.presenter_origin_id = :pres_orig_id", Programme.class)
                .setParameter("pres_orig_id", presenter_id)
                .getResultList();
        for (Programme programme : programmes) {
            //this is the critical performance death zone 
            List<Track> tracksOnThisProgramme = session
                    .createQuery("FROM track t WHERE t.programme.programme_id in :progIds", Track.class)
                    .setParameter("progIds", programme.getProgramme_id())
                    .getResultList();
            djAllProgrammes.addProgramme(new ProgrammeData(presenter.getPresenter_name(), programme.getDate(), tracksOnThisProgramme));
        }
    }
    session.getTransaction().commit();
    return djAllProgrammes;
}

Отладочная информация:

ИНФОРМАЦИЯ: Метрики сеанса

{
    33339 nanoseconds spent acquiring 1 JDBC connections;
    71991 nanoseconds spent releasing 1 JDBC connections;
    12938819 nanoseconds spent preparing 258 JDBC statements;
    88949720 nanoseconds spent executing 258 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    0 nanoseconds spent performing 0 L2C puts;
    0 nanoseconds spent performing 0 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;
    4671332 nanoseconds spent executing 1 flushes (flushing a total of 9130 entities and 0 collections);
    599862735 nanoseconds spent executing 258 partial-flushes (flushing a total of 1079473 entities and 1079473 collections)
}

Просматривая Интернет, я увидел предложение, основанное на том, что в транзакции слишком много СУЩЕСТВУЮТ сущности«использовать разбиение на страницы и меньшие приращения пакетов» - я могу найти информацию о том, что такое разбиение на страницы, но не так много, как «использование меньших приращений пакетов» означает *

Я вроде как в затруднении, когда это приложение имело отличную производительностьЯ делаю в основном то же самое, используя Apache DB Utils (облегченную оболочку jdbc), и я настолько невежественен, что даже не знаю, что искать, чтобы ускорить это.Выручить брата?

Также здесь используются бины (персистентные сущности ...?) https://pastebin.com/pSQ3iGK2

Ответы [ 2 ]

0 голосов
/ 04 января 2019

Использование ORM, такого как Hibernate, для этой задачи всегда будет медленнее, чем использование db в вашей версии прототипа, которая напрямую использует слой JDBC. Посмотрим, что происходит:

List<Presenter> result = session.createQuery("from Presenter p WHERE p.presenter_name in :names", Presenter.class)
        .setParameterList("names", names)
        .getResultList();

После того, как запрос проанализирован, объекты разрешены, а размер names определяет количество параметров, в которые он будет расширен (?,?,?...).
Затем запрос отправляется, и как только результаты поступают, у каждой из них создается две копии. Тот, который вам дан в списке результатов, и тот, который хранится внутри, чтобы проверить изменения.

for (Presenter presenter : result) {
    int presenter_id = presenter.getPresenter_id();
    List<Programme> programmes = session
            .createQuery("from programme prog WHERE prog.presenter_origin_id = :pres_orig_id", Programme.class)
            .setParameter("pres_orig_id", presenter_id)
            .getResultList();

Здесь у нас снова происходит то же самое, но на самом деле все немного хуже. Вместо повторного использования запроса вы создаете новый в каждом цикле и отбрасываете его после.
То же самое происходит во вложенном цикле.
Также, если Presenter.getPresenter_id () возвращает объект Integer вместо примитива int, вы делаете ненужную распаковку, а затем перепаковываете вызов .setParameter("pres_orig_id", presenter_id). Измените его на Integer presenter_id, если метод возвращает объект Integer. Но если это примитив int, то в этом нет необходимости, но это не повредит, поскольку единственное использование передается как Объект. Вы даже можете использовать его непосредственно в setParameter.

Таким образом, в целом, когда вы берете вызовы createQuery из цикла, вы получаете это.

@Override
public DJAllProgrammes getAllProgrammesFromDJ(Collection<String> names) {
    DJAllProgrammes djAllProgrammes = new DJAllProgrammes();
    session.beginTransaction();
    List<Presenter> result = session.createQuery("from Presenter p WHERE p.presenter_name in :names", Presenter.class)
            .setParameterList("names", names)
            .getResultList();
    TypedQuery<Programme> progByPresenterOrigin = session
                .createQuery("from programme prog WHERE prog.presenter_origin_id = :pres_orig_id", Programme.class);
    TypedQuery<Track> trackByProgrammeId = session
                    .createQuery("FROM track t WHERE t.programme.programme_id in :progIds", Track.class)
    for (Presenter presenter : result) {
        List<Programme> programmes = progByPresenterOrigin
                .setParameter("pres_orig_id", presenter.getPresenter_id())
                .getResultList();
        for (Programme programme : programmes) {
            //this is the critical performance death zone 
            List<Track> tracksOnThisProgramme = trackByProgrammeId
                    .setParameter("progIds", programme.getProgramme_id())
                    .getResultList();
            djAllProgrammes.addProgramme(new ProgrammeData(presenter.getPresenter_name(), programme.getDate(), tracksOnThisProgramme));
        }
    }
    session.getTransaction().commit();
    return djAllProgrammes;
}
0 голосов
/ 03 января 2019

Обычно говорят: OR-Mapper позволяет моделировать сущности по отношению к другим.Я видел некоторые отношения 1: N в вашем коде с докладчиком, имеющим много программ.

Имя класса «Программа» может быть первой ошибкой, потому что оно множественное.Лучше использовать «Программирование» и смоделировать отношение @OneToMany в классе «Presenter».

Когда вы это сделаете, вам придется запустить только один запрос гибернации.Найденные объекты типа «Presenter» будут содержать список / набор «Programm».Выполните итерацию по сущностям и преобразуйте их в возвращаемое значение 'DJAllProgrammes', которое должно содержать только простые значения (dto), а не ссылки на сущности.Т.е. сопоставьте сущности с dto.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...