Производительность запросов JPA EclipseLink 2

ПРИЛОЖЕНИЕ И СРЕДА

Корпоративное приложение Java EE/JSF2.0/JPA, содержащее веб-модуль и модуль EJB. Я создаю PDF-документы, содержащие оцененные данные, запрашиваемые через JPA.

Я использую MySQL в качестве базы данных с двигателем MyISAM для всех таблиц. Поставщик JPA — это EclipseLink с кешем, установленным на ALL. FetchType.EAGER используется в отношениях.

ПОСЛЕ ЗАПУСКА NETBEANS PROFILER

Результаты Profiler показывают, что чаще всего вызывается следующий метод. В этом сеансе было 3858 вызовов с ~80 секундами от запроса до ответа. Это занимает 80% процессорного времени. В таблице Question 680 записей.

public Question getQuestionByAzon(String azon) {
    try {
        return (Question) em.createQuery("SELECT q FROM Question q WHERE q.azonosito=:a").setParameter("a", azon).getSingleResult();
    } catch (NoResultException e) {
        return null;
    }
}

Сущность Question:

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Question implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Column(unique = true)
    private String azonosito;
    @Column(nullable = false)
    @Basic(optional = false)
    private String label;
    @Lob
    @Column(columnDefinition = "TEXT")
    private String help;
    private int quizNumber;
    private String type;
    @ManyToOne
    private Category parentQuestion;

    ...

    //getters and setters, equals() and hashCode() function implementations

}

Есть четыре сущности, расширяющие Question.

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

Меня интересуют предложения по оптимизации. Не стесняйтесь спрашивать, нужна ли вам дополнительная информация!

ИЗМЕНИТЬ См. мой ответ, обобщающий лучшие результаты

Заранее спасибо!


person Daniel Szalay    schedule 04.02.2011    source источник
comment
Вы проверили, где замедление - на стороне базы данных или на стороне приложения?   -  person axtavt    schedule 04.02.2011
comment
Еще нет. Я постараюсь следить за этим в Profiler, если это возможно. (Я новичок в этом)   -  person Daniel Szalay    schedule 04.02.2011


Ответы (4)


Использование LAZY — хорошее начало, я бы порекомендовал вам всегда делать LAZY, если вас вообще беспокоит производительность.

Также убедитесь, что вы используете переплетение (агент Java SE, Java EE/Spring или статическое), так как LAZY OneToOne и ManyToOne зависят от этого.

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

Вы должны сделать запрос NameDQuery, а не использовать динамический запрос. В EclipseLink вы также можете включить кеширование запроса для запроса (если это именованный запрос), это позволит кешировать результаты запроса.

person James    schedule 07.02.2011
comment
На самом деле это не один и тот же запрос, выполняемый снова и снова. Я имею в виду, что он извлекает один экземпляр из 680 объектов. Эти объекты необходимы для (множества) расчетов, а результаты отображаются на (множестве) диаграмм в конце. Что я сделал: 1. написал @NamedQuery для Question, 2. включил общий кеш в persistence.xml, 3. добавил подсказку @QueryHint(name = "javax.persistence.cache.storeMode", value = "REFRESH") к @NamedQuery; однако не совсем уверен, нужен ли последний, но в результате эти шаги улучшили производительность. Время выполнения сокращается, процедура повторяется. - person Daniel Szalay; 09.02.2011
comment
Я установил javax.persistence.cache.storeMode на USE вместо REFRESH, так как эти объекты не меняются. Также установите javax.persistence.cache.retrieveMode на USE. Производительность++, спасибо! :) Ссылка: en.wikibooks.org/wiki/Java_Persistence/Caching - person Daniel Szalay; 09.02.2011
comment
Также добавлено кэширование запросов EclipseLink hinz @QueryHint(name = QueryHints.QUERY_RESULTS_CACHE, value = HintValues.TRUE), как было предложено; очень хорошие результаты. - person Daniel Szalay; 09.02.2011
comment
@DanielSzalay, можете ли вы поделиться подробностями, связанными с вашим комментарием выше, 2. включить общий кеш в файле persistence.xml? - person Howard; 28.11.2012
comment
@Howard К сожалению, у меня больше нет этого persistence.xml. Я думаю, это то, что я вставил в тег <properties/>: <property name="javax.persistence.cache.storeMode" value="USE" />. И если я правильно помню, я добавил @QueryHint к классу сущностей, который нужно кэшировать. - person Daniel Szalay; 29.11.2012

У вас есть уникальный индекс в столбце azonosito в вашей базе данных. Может быть, это поможет. Я бы также посоветовал получать только те поля, которые вам действительно нужны, поэтому, возможно, некоторые из них могут быть ленивыми, например, категория.

person Marcin Michalski    schedule 04.02.2011
comment
Убедитесь, что индекс действительно существует в вашей таблице, возможно, уникальная аннотация была добавлена ​​после создания таблицы? - person Sam Barnum; 04.02.2011
comment
Я тоже добавил его вручную, а также попробовал FULLTEXT индексацию. Ни один из них не улучшил производительность заметно. - person Daniel Szalay; 04.02.2011
comment
Я изменил fetch=FetchType.EAGER на fetch=FetchType.LAZY во всех отношениях, для которых он был установлен. Производительность на порядок лучше: ~80 с -> ~14 с. На самом деле есть и другие классы сущностей, которые содержат списки Question. - person Daniel Szalay; 04.02.2011
comment
Просто упомянуть. Я постараюсь использовать ObjectDB для повышения производительности приложений, так как могут быть гораздо большие запросы. - person Daniel Szalay; 04.02.2011

Поскольку изменение типа отношения выборки на LAZY значительно улучшило производительность вашего приложения, возможно, у вас нет индекса для внешнего ключа этого отношения. Если это так, вам нужно его создать.

person axtavt    schedule 05.02.2011
comment
Я пробовал это, и это не сильно улучшило производительность. Так что я предполагаю, что дизайн классов сущностей делает FetchType.EAGER очень дорогим. Тем не менее, это отличное предложение, так как это может еще больше повысить производительность при установке FetchType.LAZY. Спасибо! - person Daniel Szalay; 05.02.2011

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

Прежде всего, я установил столбец azonosito в качестве первичного ключа и соответствующим образом изменил свои объекты. Это необходимо, поскольку кеш объектов EclipseLink работает с em.find:

public Question getQuestionByAzon(String azon) {
    try {
        return em.find(Question.class, azon);
    } catch (NoResultException e) {
        return null;
    }
}

Теперь, вместо использования QUERY_RESULT_CACHE на @NamedQuery, я настроил объект Question следующим образом:

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Cache(size=1000, type=CacheType.FULL)
public abstract class Question implements Serializable { ... }

Это означает, что кэш объектов максимального размера 1000 будет поддерживаться для всех Question сущностей.

Результаты Profiler: ~16 000 вызовов

QUERY_RESULT_CACHE: ~28000ms

@Cache(size=1000, type=CacheType.FULL): ~7500ms

Конечно, время выполнения сокращается после первого выполнения.

person Daniel Szalay    schedule 12.02.2011
comment
Интересный. Как вы проверили QUERY_RESULT_CACHE ~ 28000 мс? Я новичок в Java Visual VM (jvisualvm) - person Howard; 29.11.2012
comment
@Howard Если я правильно помню, я прикрепил профилировщик к своему приложению, и поэтому я мог видеть время выполнения метода. Затем я просто сравнил время выполнения различных настроек кеша. - person Daniel Szalay; 29.11.2012