Какой самый быстрый запрос findByName с hibernate? - PullRequest
2 голосов
/ 15 июня 2010

Я уверен, что смогу улучшить производительность следующего запроса findByName hibernate:

public List<User> findByName(String name) {
  session.createCriteria(User.class).add(Restrictions.eq("name", name)).list();
}

Узким местом является метод findByName, и я не могу использовать вместо него идентификатор.

В моемЯ знаю, что имя уникально, но добавление аннотации Index к атрибуту name не улучшило производительность.Я сделал следующее:

class User {
  @Index(name = "nameIdx")
  private String name;      
}

Каким образом я должен улучшить его или, что еще важнее: каким образом я должен улучшить его первым?Мне понадобится полный объект со всеми коллекциями (layz или нет) и deps этого класса.

Или я могу улучшить его, если я хочу несколько объектов User (и знаю несколько имен)?

Update1:

Аннотация @Index не повысила производительность, поскольку в базе данных уже был индекс из-за моей уникальной аннотации ограничения:

@UniqueConstraint(columnNames = {"name"})

Обновление2:

  1. Внимательно прочитайте ответы!

  2. С помощью ведения журнала SQL я увидел, что реальноепроблема заключалась в том, что было поднято много операторов update и insert, хотя я не фиксировал и не очищал транзакцию.Причиной этого было то, что я сделал (в цикле):

    User u = findByName(name);
    if(u == null) 
       attach(u = new User(name));
    

    , и поэтому hibernate должен сбрасывать вновь созданных пользователей в базу данных перед каждым запросом findByName.Я решил эту проблему с помощью моего собственного обхода кэша (LinkedHashMap).

  3. Еще одно улучшение, которое я сделал с помощью совета Йенса Шаудера:

    public Collection<User> findByNames(Collection<String> names) {
       return session.createCriteria(User.class).
              add(Restrictions.in("name", names)).list();
    }
    
  4. Ещеулучшение можно было бы сделать, указав часть коллекции пользователей как не ленивую:

    @LazyCollection(LazyCollectionOption.FALSE)
    

    Прочтите этот ответ , чтобы получить еще лучший вариант.

  5. Последнее и самое важное для меня: замена элементов SortedSet списком и выполнение в методе getItems следующего действия:

    Set set = new LinkedHashSet(items);
    items.clear();
    items.addAll(set);
    Collections.sort(items, itemComparator);
    return Collections.unmodifiableCollection(items);
    

    , при этом hibernate может работать с коллекцией элементов (т. Е. Добавлять) без загрузки всей коллекции из базы данных.

@ Pascal Thivent и @Jens Schauder : огромное спасибо!Извините, что я могу принять только один ответ: - /

Полезные настройки ведения журнала:

log4j.logger.org.hibernate.tool.hbm2ddl=INFO, StdoutApp
log4j.logger.org.hibernate.SQL=INFO, StdoutApp
# additionally provide the information which parameters will be bound:
log4j.logger.org.hibernate.type=TRACE

Еще одна полезная ссылка .

Ответы [ 2 ]

3 голосов
/ 15 июня 2010

Вы не предоставляете достаточно информации для полного ответа, но вот несколько идей:

  • вы можете использовать вместо этого идентификатор? Hibernate подготовит запросы для выбора по идентификатору, поэтому они будут (немного) быстрее, чем другие запросы
  • правильно ли проиндексировано имя? Для этого запроса у него должен быть уникальный ключ (вы намекаете, вы ожидаете один результат). Конечно, такой индекс снижает производительность при вставке, обновлении и удалении.
  • когда мы подходим к ссылкам, это зависит от того, что вы подразумеваете под производительностью: время до возвращения заявления? Тогда вам следует использовать ленивую загрузку. Это делает первое утверждение быстрее и, следовательно, быстрее. Конечно, у вас будет больше утверждений после того, как ссылки станут обезвоженными. В противном случае (некоторые) стремительная загрузка, вероятно, быстрее, хотя это сильно зависит от деталей.
  • использовать кэширование, это может особенно помочь для ссылок, если они могут быть получены из кэша.
  • настроить вашу базу данных. Дайте ему достаточно памяти, чтобы все время держать в памяти.
  • настроить вашу сеть. При небольших запросах, подобных показанному, задержка может быть проблемой
  • удалить сеть, поместив БД на тот же компьютер, что и код. Предполагая, что он достаточно большой.

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


ОБНОВЛЕНИЕ на основе комментария:

При настройке первый вопрос: что нам нужно настроить? Это преобразование критериев в оператор SQL? Если это так, то предоставление SQL-оператора напрямую может помочь.

Это фактическое выполнение оператора sql? В этом случае первым делом будет определение оператора sql, полученного из опубликованного кода.

Я никогда не видел реального случая, когда хранимая процедура делала вещи быстрее. Конечно, это не значит, что таких случаев не существует. Но оптимизаторы современных rdbms довольно умны.

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

Это скажет вам, выполняются ли операторы SQL и занимает ли это много времени и является ли это оператором SQL, который вызывает проблему.

Большую часть времени SQL-операторы виновны в плохой производительности, но не стоит спешить с выводами.


Обновление в части многих имен:

Вы можете использовать выражение InExpression: http://docs.jboss.org/hibernate/core/3.3/api/org/hibernate/criterion/InExpression.html, чтобы найти несколько объектов за один раз. Это будет быстрее, чем отдельные запросы.

2 голосов
/ 16 июня 2010

В моем случае я знаю, что имя уникально, но добавление аннотации индекса к атрибуту имени не улучшило производительность.Узким местом является метод findByName.

Я не поверю в это ... пока вы не покажете цифры, доказывающие, что я не прав :) Итак:

  • Дважды проверьте, чтобы индекс был сгенерирован (проверьте операторы DDL и базу данных).Для этого запроса вам понадобится индекс для этого столбца.
  • Проверьте план запроса для сгенерированного запроса (должно быть что-то вроде SELECT * FROM USER u WHERE u.NAME = 'foo') и время выполнения.

Позже вы можете рассмотреть возможность активации кэша второго уровня и кэширования запроса.Но база данных - это то место, с которого нужно начинать (слишком раннее кэширование просто скрывает реальную проблему).

И измеряйте вещи! Если вы не можете измерить это, вы не можете улучшить его.- Лорд Кельвин .

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