Запрос из приложения Hibernate не использует индексы БД - PullRequest
5 голосов
/ 04 января 2012

Я пытаюсь решить проблему производительности моего приложения. Запрос hibernate генерирует, имеет вид:

select * 
from ( 
    select this.a, this.b, this.state, this.id 
    from view_user this 
    where this.state=:1 order by this.a asc, this.b
) 
where rownum <= :2

, где

  • id - это первичный ключ
  • есть объединенный, уникальный индекс для (a, b, id).
  • view_user имеет ~ 2 миллиона записей
  • view_user выполняет дальнейшие объединения с другими таблицами

Выпуск

Вышеприведенный запрос выполняет - быстро от SQLDeveloper - быстро из небольшого Java-приложения в спящем режиме - очень медленно (> 100 раз медленнее) из приложения в спящем режиме - значения для переменных связывания 2 соответственно 30 (происхождение rownum из подкачки) - запрос гибернации имеет «форму» выше. На самом деле в представлении есть около 20 столбцов.

Текущее состояние анализа

  • план запроса показывает, что индекс используется, когда запрос поступает из SQlDeveloper или "небольшого Java-приложения".
  • план запроса показывает, что полное сканирование таблицы выполняется, если запрос поступает из приложения hibernate
  • Трассировка БД показывает только два различия: настройки NLS (от SQLDeveloper) и немного другое форматирование (пробелы). Все остальное вроде бы тоже самое ...

Версия

  • Спящий режим: 2.1.8
  • jdbc драйвер: используется ojdbc14, 5 и 6. Не имеет значения
  • Oracle: 10.2 и 11. Не имеет значения

=> Я рад каждому намеку на этот вопрос. Что меня беспокоит, так это то, что трассировка БД не показала каких-либо различий ... Да, похоже, это что-то про спящий режим. Но что? Как обнаружить?


Для полноты вот запрос гибернации (из журнала):

Select * from ( 
    select this.USER_ID as USER_ID0_, this.CLIENT_ID as CLIENT_ID0_, 
    this.USER_NAME as USER_NAME0_, this.USER_FIRST_NAME as USER_FIR5_0_, this.USER_REMARKS as 
    USER_REM6_0_, this.USER_LOGIN_ID as USER_LOG7_0_, this.USER_TITLE as USER_TITLE0_, 
    this.user_language_code as user_lan9_0_, this.USER_SEX as USER_SEX0_, 
    this.USER_BIRTH_DATE as USER_BI11_0_, this.USER_TELEPHONE as USER_TE12_0_, 
    this.USER_TELEFAX as USER_TE13_0_, this.USER_MOBILE as USER_MO14_0_, 
    this.USER_EMAIL as USER_EMAIL0_, this.USER_ADDRESSLINE1 as USER_AD16_0_, 
    this.USER_ADDRESSLINE2 as USER_AD17_0_, this.USER_POSTALCODE as USER_PO18_0_, 
    this.USER_CITY as USER_CITY0_, this.USER_COUNTRY_CD as USER_CO20_0_, 
    this.USER_COUNTRY_NAME as USER_CO21_0_, this.USER_STATE_ID as USER_ST24_0_, 
    this.USER_STATE as USER_STATE0_, this.USER_TEMP_COLL_ID as USER_TE26_0_, 
    this.USER_TEMP_COLL_NAME as USER_TE27_0_, this.UNIT_ID as UNIT_ID0_, 
    this.CLIENT_NAME as CLIENT_38_0_, this.PROFILE_EXTID as PROFILE39_0_
    from VIEW_USER this
    where this.USER_STATE_ID=:1 order by this.USER_NAME asc, this.USER_FIRST_NAME asc
) 
where rownum <= :2

Уникальный индекс превышает user_name, user_first_name, user_id.

Ответы [ 6 ]

4 голосов
/ 16 февраля 2012

Я сталкивался с подобной ситуацией, так что это может иметь отношение к вам.

Драйвер JDBC меняет ваши параметры на unicode, поэтому varchar становится nvarchar, когда попадает в базу данных.Если вам повезет, SQL 2008 SP1 поймает это и преобразует обратно.Но SQL 2000 и SQL 2005 не будут работать, и оптимизатор запросов выполнит полное сканирование таблицы, игнорируя ваши индексы.

Возможно, вы сможете исправить это на уровне JDBC, добавив в ваше соединение параметр соединения sendStringParametersAsUnicode=FALSEстрока.

1 голос
/ 22 января 2012

Окончательное решение вопроса было c3p0.Мы не смогли отследить, почему были выбраны неправильные планы запросов.Наше последнее предположение состоит в том, что это как-то связано с инициализацией соединения dbcp.Но из-за озера времени мы опробовали c3p0.Первый опыт:

  • проблема не появляется
  • быстрее, потому что намного более агрессивна с точки зрения использования maxConnections, настроенного для пула
  • , и из-за вышеизложенногонаше полное приложение теперь быстрее.Это основано на общем впечатлении от использования приложения, а также на наших различных нагрузочных тестах

Итак, в настоящий момент мы с нетерпением тестируем различные сценарии с c3p0, чтобы быть готовыми к продуктивным системам.

Спасибо за все вклады!

1 голос
/ 04 января 2012

SQL, который вы предоставляете, не похож на сгенерированный Hibernate запрос.Вы уверены, что это не рукописный запрос?

Если вы хотите использовать Hibernate, вы можете использовать setMaxResults (), чтобы ограничить количество возвращаемых строк.

Если вы хотите использовать рукописный запрос, ядумаю, что вы хотите что-то вроде этого:

select * 

из (выберите this.a, this.b, this.state, this.id из view_user this, где this.state =: 1 заказ this.a asc, this.b) где rownum <=: 2 </p>

0 голосов
/ 26 августа 2015

У нас была та же проблема: при выполнении запроса из SQL Developer план объяснения показал, что индекс использовался, но при выполнении запроса в приложении через Hibernate индекс игнорировался.

Мы решили эту проблему, добавив подсказку в запрос к оптимизатору Oracle для использования рассматриваемого индекса.

0 голосов
/ 18 октября 2013

Существует ответ выше / ниже о nvarchars с настройкой для SQL Server - вот аналогичная настройка для Oracle ...

Убедитесь, что кто-то не установил свойство oracle.jdbc.defaultNChar = true

Иногда это делается для решения проблем с юникодом, но это означает, что все столбцы обрабатываются как nvarchars. Если у вас есть индекс для столбца varchar, он не будет использоваться, потому что oracle должен использовать функцию для преобразования кодировки символов.

0 голосов
/ 11 января 2012

После долгих отладок и попыток, у меня есть ответ, как решить эту проблему. Однако пока не могу объяснить, в чем конкретно проблема.

Кажется, проблема в уровне пула соединений JDBC. Мой конфиг был:

  • initialSize = 0
  • minIdle = 10

Используя вышеупомянутую конфигурацию, у нас были симптомы, описанные в описании проблемы. Решение до сих пор, кажется, состоит в том, чтобы установить minIdle равным initialSize. В противном случае соединение, кажется, инициализируется как-то иначе, и мы видим снижение производительности. Итак, мы установили оба на «10».

Я сейчас анализирую логи. На сервере БД файлы трассировки не показывают никаких отличий. Трассировка JDBC также не показала никакой интересной информации.

Гугл, я видел, что на многих примерах конфигурации установлен minIdle = initialSize. Кроме того, я нашел две цитаты (весенний документ, документ VMware), касающиеся настроек JDBC:

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

Обратите внимание, что последнее предложение "производное от initialSize" встречается не во всех документах для minIdle. В большинстве документов по умолчанию указано «0».

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