JDB C Query vs JPA Query Performance - PullRequest
       25

JDB C Query vs JPA Query Performance

4 голосов
/ 20 апреля 2020

У меня проблемы с производительностью при чтении тысяч записей из базы данных. Я заметил, что чистый запрос JDB C намного быстрее, чем собственный запрос JPA.

Вот запрос

select ID, COL_A, COL_B, COL_C, COL_D, COL_E, COL_F from MY_SUPER_VIEW_V v 
where 1=1 
and v.ID in (:idList)
and v.DATE_FROM <= :date
and v.DATE_TILL >= :date;

Этот запрос возвращает около 38 000 записей.

В idList содержится более 1000 записей, и, поскольку я использую Oracle БД, его необходимо разделить на n запросов.

Кроме того, у меня есть метод, который покрывает результат Object [] для моего List<Entity>.

Чтобы понять проблему с производительностью, я создал чистый запрос JDB C и запрос JPA Native соответственно для сравнения результатов.

Вот значения времени.

################ getScoresPureJDBCWithListIds ################
List of Ids retrieved. It took: 00:00:00.096 to execute query on DB using JDBC
It took: 00:00:01.180 to execute query on DB using JDBC query
Creating 24206 Scores records from DB result It took: 00:00:04.440
It took: 00:00:01.038 to execute query on DB using JDBC query
Creating 14445 Scores records from DB result It took: 00:00:04.307
################ getScoresJPANativeQueryWithListIds ################
It took: 00:06:09.450 to execute query on DB using JPA Native query
Creating 24206 Scores records from DB result It took: 00:00:00.009
It took: 00:04:04.879 to execute query on DB using JPA Native query
Creating 14445 Scores records from DB result It took: 00:00:00.007

С аналитикой Hibernate

################ USING FETCH_SIZE: 2000 ################
################ getSmartESGScoresPureJDBCWithListCsfLcIds ################
List of Securities CsfLcId retrieved. It took: 00:00:00.296 to execute query on DB using JDBC
It took: 00:00:11.940 to execute query on DB using JDBC query
Creating 24206 Smart Esg Scores records from DB result It took: 00:00:02.670
It took: 00:00:13.570 to execute query on DB using JDBC query
Creating 14445 Smart Esg Scores records from DB result It took: 00:00:02.553
################ getSmartESGScoresJDBCTemplateWithListCsfLcIds ################
List of Securities CsfLcId retrieved. It took: 00:00:00.087 to execute query on DB using JDBC
Creating 24206 Smart Esg Scores records from DB result It took: 00:00:04.063
Creating 14445 Smart Esg Scores records from DB result It took: 00:00:04.064
################ getSmartESGScoresJPANativeQueryAsESGenius with hint fetch size 2000 ################
2020-04-22 09:36:30.830  INFO 13262 --- [           main] i.StatisticalLoggingSessionEventListener : Session Metrics {
    1232369 nanoseconds spent acquiring 1 JDBC connections;
    0 nanoseconds spent releasing 0 JDBC connections;
    1448702 nanoseconds spent preparing 1 JDBC statements;
    3992364 nanoseconds spent executing 1 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;
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
    0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
}
List of Securities CsfLcId retrieved. It took: 00:00:00.261 to execute query on DB using JDBC
2020-04-22 09:47:23.739  INFO 13262 --- [           main] i.StatisticalLoggingSessionEventListener : Session Metrics {
    73670 nanoseconds spent acquiring 1 JDBC connections;
    0 nanoseconds spent releasing 0 JDBC connections;
    805772 nanoseconds spent preparing 1 JDBC statements;
    651947762290 nanoseconds spent executing 1 JDBC statements; ==> 10 minutes
    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;
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
    0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
}
It took: 00:10:52.898 to execute query on DB using JPA Native query
Creating 24206 Smart Esg Scores records from DB result It took: 00:00:00.018
2020-04-22 09:56:00.792  INFO 13262 --- [           main] i.StatisticalLoggingSessionEventListener : Session Metrics {
    2758010 nanoseconds spent acquiring 1 JDBC connections;
    0 nanoseconds spent releasing 0 JDBC connections;
    3096653 nanoseconds spent preparing 1 JDBC statements;
    516148003151 nanoseconds spent executing 1 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;
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
    0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
}
It took: 00:08:37.032 to execute query on DB using JPA Native query
Creating 14445 Smart Esg Scores records from DB result It took: 00:00:00.006

Для запроса JDB C я вижу 1), что выполнение запроса выполняется довольно быстро, но 2) обработка каждого ResultSet элемент в al oop занимает большую часть времени 00:09 секунд int total

С другой стороны для собственного запроса JPA 1) выполнение запроса путем вызова метода query.getResultList () занимает много времени время 10:14 секунд с другой стороны 2) процесс Каждый результат здесь довольно быстрый. Аналитика показывает, что на выполнение 1 оператора JDB C тратится огромное количество времени. Даже с FETCH_SIZE = 2000 ничего существенно не изменилось.

Почему JPA Native довольно медленный, если сравнивать с чистым JDB C? Было бы преобразование типов? В моем случае я говорю о varchar2 и числах. Я ожидал идентичные результаты для JDB C. но от 8 секунд до 10 минут это много.

Что я могу сделать, чтобы улучшить собственный запрос JPA?

Ответы [ 3 ]

3 голосов
/ 25 апреля 2020

Похоже, вы сравниваете два разных запроса, что может привести к тому, что в базе данных появятся разные планы запросов.

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

  • Включите ведение журнала отладки для вашего Java приложения, включая Hibernate и Oracle JDB C драйвер, как объяснено в их документации
  • Посмотрите, откуда приходит задержка: база данных, сеть или ваше приложение Java? Если вы сомневаетесь, проверьте сетевой трафик c с Wireshark по обеим сторонам соединения или проверьте статистику базы данных Oracles по медленным / тяжелым запросам до и после проблем c запросов
  • Если проблема связана с медленной базой данных убедитесь, что параметры вашего запроса имеют совпадающие типы с индексом вашей базы данных
  • Если вы уверены, что проблема не связана с сетью и базой данных, а ведение журнала отладки не поможет вам в дальнейшем использовать расширенные инструменты, такие как профилировщик ЦП, например: JVisualVM
  • Если у вас все еще есть проблемы, может быть, у вас есть какие-то проблемы с экстремальной памятью, такие как нехватка системной памяти, вызывающая перестановку, или очень частая полная сборка мусора, которую вы можете увидеть в журнале сбора мусора
1 голос
/ 30 апреля 2020

Обратите внимание, что если вы хотите сравнить две концепции, вы должны попытаться изолировать основную функцию и избавиться от других факторов, которые могут повлиять на результат.

Чтобы увидеть , если JDB C запрос и собственный запрос JPA отличаются по поведению Я бы предложил следующий сценарий:

  • использовать только один запрос со списком из 1000 элементов

  • использовать обычную таблицу вместо представления

Вот простая настройка для проверки исполнения. Таблица содержит 50 строк для каждого GRP_ID, что приводит к получению 50K строк для 1000 ключей (см. Ниже скрипт для настройки таблицы)

List params = (13001L..14000L)
def query = session.createNativeQuery("select * from tab where grp_id in (:paramsList) ")
query.setFetchSize(2000)
query.setParameterList("paramsList", params);
result = query.getResultList();

Пример выполнения показывает этот результат

 got 50000 rows in 1.388 seconds

Итак, я думаю, что нет необходимости повторять тест с простым JDB C, вы увидите сопоставимый результат.

Что более интересно, повторить прогон и удалите строку

query.setFetchSize(2000)

, которая эффективно сбросит размер выборки к значению по умолчанию (в моем случае - 20), результат для тех же данных будет

 got 50000 rows in 1 minutes, 0.903 seconds

1) Таким образом, размер выборки является наиболее подходящим объяснением наблюдаемого поведения . Важно убедиться, что диск JDB C получил правильное значение и использует его - если вы сомневаетесь, вы должны использовать трассировку 10046, чтобы увидеть, какой размер выборки использует базу данных. Но для меня вышеупомянутое утверждение сработало отлично.

2) Нет существенной разницы между собственным запросом JPA и написанным вручную JDB C execute + выборкой подготовленного оператора , который бы объясните свое наблюдение Оба выполняют выполнение оператора в базе данных , за которым следует число выборок - количество зависит от используемого размера выборки

3) Конечно, представление также может иметь влияние, но это будет разница в запросе - не между JDB C v. JPA .

4) Вы не упомянули об этом, поэтому я не буду вдаваться в подробности и предполагаю, что ваше представление не содержит столбцов CLOB , Это, конечно, может сыграть свою роль.

5) Последний пункт в вашем упоминании два запроса - используете ли вы два независимых запроса или один запрос с OR объединены в списке? Вы не предоставляете подробности, поэтому это трудно комментировать. В любом случае два независимых запроса не должны иметь никакого влияния.

Сказав это одно предупреждение.

Ограничение подсчета списка IN имеет свое назначение. Для и объявления Ho c script допустимо использовать большой выбор списка IN, но для регулярного выполнения запроса это может быть проблема синтаксического анализа . Почему?

Вы используете переменные связывания, чтобы иметь возможность рассматривать следующие очереди как одно утверждение (которое анализируется только один раз)

select * from tab where ID = 1
select * from tab where ID = 2

, что приводит к

select * from tab where ID = ?

Но после двух запросов (с разной длиной списка IN) остается разным и должен подвергаться дополнительному анализу

select * from tab where ID in ( ? )
select * from tab where ID in ( ?, ? )

Так что повторите, если для вашей цели с 30K строк + Hibernate является лучшим вариантом

Hibernate был разработан, чтобы элегантно получить представление о необходимости с использованием SQL, который большинством разработчиков считают круто думать (в отличие от большинства сотрудников БД, имеющих противоположное значение;).

Эта концепция работает отлично, чем проще случай использования. С другой стороны, для пакетной обработки иногда лучше подойти к нему напрямую с SQL

Данные испытаний

create table tab as 
select 
rownum id,
trunc(rownum /  50) +1 grp_id,
rpad('x',100,'y') pad
from dual connect by level <= 1000000;
create index idx on tab(grp_id);
1 голос
/ 29 апреля 2020

JDB C, как правило, быстрее, чем JPA, но в JPA вы можете извлечь выгоду из кэширования, и таким образом повысить производительность.

Я не знаю цели этого запроса и как он используется (отчетность?) , но вы должны рассмотреть возможность использования других критериев, а не просто список очень многих идентификаторов. Я сомневаюсь, что некоторые пользователи выбрали более 1000 идентификаторов вручную, поэтому я думаю, что они выбираются партиями по некоторым другим критериям Попробуйте использовать это creatia вместо этого.

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