Подтвердить что ты не робот

Почему Hibernate query.list() медленный?

Я использую Hibernate 4.1.6 и имею проблемы со скоростью создания списка. Я выполняю следующий запрос.

public void doQuery(final Baz baz){
  final Query query = getSessionFactory().getCurrentSession().createQuery(
          "select c.id, foo.someValue from Foo as foo "+
          "join foo.a as a"+
          "join foo.b as b "+
          "join b.c as c "+
          "where baz=:baz"
          );
  query.setParameter("baz", baz);
  Long start=System.currentTimeMillis();
  final List<Object[]> list = query.list();
  Long end=System.currentTimeMillis();
  System.out.println((end-start));
}

Я установил hibernate для отладки, чтобы получить фактический запрос, который отправляется в базу данных. Я выполнил этот запрос непосредственно в базе данных и возвратил 23 000 строк в 0,015 мс. Итак, я предполагаю, что вопрос не является проблемой. В приведенном выше примере показано, что для создания этого списка требуется ~ 32 секунды. Есть ли что-то, что можно сделать, чтобы ускорить это?

Обновление. Я попытался использовать метод createSQLQuery() с помощью запроса отладки спящего режима и работал так же медленно, как метод createQuery().

Обновление: я пробовал использовать сеанс без состояния, но он работал так же медленно.

Обновление: я вывел некоторые статистические данные (установив флаг hibernate.generate_statistics в true), но мне ничего не кажется тревожным:

Hibernate SessionFactory Statistics [
    Number of connection requests[4]
    Number of flushes done on the session (either by client code or by hibernate[3]
    The number of completed transactions (failed and successful).[3]
    The number of transactions completed without failure[3]
    The number of sessions your code has opened.[4]
    The number of sessions your code has closed.[3]
    Total number of queries executed.[4]
    Time of the slowest query executed.[28258]
    the number of collections fetched from the DB.[6]
    The number of collections loaded from the DB.[6]
    The number of collections that were rebuilt[0]
    The number of collections that were 'deleted' batch.[0]
    The number of collections that were updated batch.[0]
    The number of your objects deleted.[0]
    The number of your objects fetched.[1]
    The number of your objects actually loaded (fully populated).[204]
    The number of your objects inserted.[1]
    The number of your object updated.[0]
]

Hibernate SessionFactory Query Statistics [
    total hits on cache by this query[0]
    total misses on cache by this query[0]
    total number of objects put into cache by this query execution[0]
    Number of times this query has been invoked[1]
    average time for invoking this query.[28258]
    maximum time incurred by query execution[28258]
    minimum time incurred by query execution[28258]
    Number of rows returned over all invocations of this query[23303]
]

Обновление. Я вижу ту же медлительность при выполнении следующей() из ScrollableResults из собственного запроса. Обратите внимание, что я ничего не делаю в цикле.

    ScrollableResults results = query.scroll();
    Long start=System.currentTimeMillis();
    while (results.next()) {
       //do nothing
    }
    Long end=System.currentTimeMillis();
    System.out.println((end-start));
4b9b3361

Ответ 1

Я не на 100% уверен в этом ответе, так как вопросы настройки/оптимизации всегда так сложно определить.

Однако, основываясь на том, что вы включили show_sql, извлекли запрос и запустили его непосредственно в базе данных и увидели подсекущие результаты против времени выполнения через Hibernate Query, я сосредоточен на том, как Hibernate создает и увлажняет объекты, вызванные вызовом query.list().

Вот еще один пользователь, который упомянул о проблемах с производительностью Query в Hibernate и увидел резкое увеличение производительности за счет добавления в POJO полнофункциональных конструкторов (конструкторов, которые принимают значение для каждого поля): Простой запрос на спящий режим возвращается очень медленно

Похоже, они наткнулись на это исправление, и не было четкого понимания того, почему это сработало. Были предположения о том, что Hibernate должен использовать отражение для обнаружения свойств. Мне любопытно, и я планирую вникнуть в исходный код Hibernate, чтобы лучше понять это, когда у меня есть шанс. Тем временем, однако, вы можете захотеть заглянуть в добавление этих полных конструкторов с параметрами для всех ваших атрибутов класса POJO и посмотреть, не изменилось ли это.

Пожалуйста, дайте мне знать, что вы найдете, так как меня очень интересует оптимизация производительности Hibernate. Спасибо!

Ответ 2

Если запросы (с show_sql), похоже, не имеют проблемы, возможно, это в коде. Запустите VisualVM (поставляется с JDK как jvisualvm) и используйте его профилировщик процессора, чтобы выяснить, какие методы занимают самое длинное время.

Ответ 3

Я запросил этот запрос непосредственно в базе данных и возвратил 23 000 строк в 0,015 мс. Итак, я предполагаю, что вопрос не является проблемой.

Это может быть преждевременным, поскольку время выполнения запроса зависит от намного большего, чем текст запроса. Даже если запросы выполняются на одних и тех же данных, откуда вы знаете, что в базе данных используется один и тот же план выполнения? Откуда вы знаете, что он получает столько же кеш-данных в своем кеше? Например, hibernate использует подготовленные инструкции при разговоре с базой данных, но вы, вероятно, этого не сделали. В Oracle планы выполнения кэшируются текстом запроса, поэтому другой текст запроса означает недавно вычисленный план выполнения. Поскольку кешированный план выполнения, возможно, был сформирован на основе разных параметров запроса, он может очень отличаться - и это может изменить время выполнения на порядки. Обратите внимание, что я не говорю, что это база данных, но я бы не стал снижать эту возможность.

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

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

Если это в JVM, используйте Profiler для определения узкого места производительности.

Ответ 4

Мы столкнулись с подобной проблемой, не знаю, если это связано. В принципе, поскольку мы добавляли новые SessionFactorys один раз за запрос, он выполнял такие запросы, как:

 select streamref0_.UUID as UUID145_, streamref0_.Tape_TapeId as Tape2_145_ from StreamRefToTape streamref0_ where streamref0_.UUID=?

Вы заметите большие цифры там. Оказывается, он увеличивается один раз за новый сеанс factory. Во всяком случае, это заставляло оракула тратить все свое время на разработку нового плана для каждого запроса (он сообщил, что процессор почти все в режиме "жесткого разбора" генерирует новые планы - я думаю, что Oracle медленно генерирует планы, которые он не видел до?). Исправление в этом конкретном случае заключалось в том, чтобы использовать один и тот же factory вместо нового. См. Также Hibernate производит различный SQL для каждого запроса

http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:2588723819082 объясняет сложные анализы, которые, по-видимому, плохие.

Еще одно возможное решение - использовать "raw sql" (jdbc) или, возможно, необработанные sql-запросы в спящем режиме, хотя это, похоже, не помогло решить проблему в этом конкретном случае...