3 Распространенные проблемы с производительностью Hibernate и их поиск в файле журнала

1. Вступление

Возможно, вы читали некоторые жалобы на плохую производительность Hibernate или, возможно, вы сами боролись с некоторыми из них. Я использую Hibernate уже более 15 лет и столкнулся с более чем достаточным количеством этих проблем.

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

2. Найти и устранить проблемы с производительностью

2.1. Журнал SQL-операторов в производстве

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

Это регистрация операторов SQL в производственной среде.

Написание некоторых операторов журнала не кажется большой проблемой, и есть много приложений, которые делают именно это. Но это крайне неэффективно, особенно через System.out.println , как это делает Hibernate, если для параметра show sql в конфигурации Hibernate установлено значение true__:

Hibernate: select
    order0__.id as id1__2__,
    order0__.orderNumber as orderNum2__2__,
    order0__.version as version3__2__
  from purchaseOrder order0__
Hibernate: select
    items0__.order__id as order__id4__0__0__,
    items0__.id as id1__0__0__,
    items0__.id as id1__0__1__,
    items0__.order__id as order__id4__0__1__,
    items0__.product__id as product__5__0__1__,
    items0__.quantity as quantity2__0__1__,
    items0__.version as version3__0__1__
  from OrderItem items0__
  where items0__.order__id=?
Hibernate: select
    items0__.order__id as order__id4__0__0__,
    items0__.id as id1__0__0__,
    items0__.id as id1__0__1__,
    items0__.order__id as order__id4__0__1__,
    items0__.product__id as product__5__0__1__,
    items0__.quantity as quantity2__0__1__,
    items0__.version as version3__0__1__
  from OrderItem items0__
  where items0__.order__id=?
Hibernate: select
    items0__.order__id as order__id4__0__0__,
    items0__.id as id1__0__0__,
    items0__.id as id1__0__1__,
    items0__.order__id as order__id4__0__1__,
    items0__.product__id as product__5__0__1__,
    items0__.quantity as quantity2__0__1__,
    items0__.version as version3__0__1__
  from OrderItem items0__
  where items0__.order__id=?

В одном из моих проектов я за несколько минут улучшил производительность на 20%, установив для show sql значение false__. Это то достижение, о котором вы хотели бы сообщить на следующем очередном заседании up

Совершенно очевидно, как вы можете исправить эту проблему производительности. Просто откройте свою конфигурацию (например, файл persistence.xml) и установите для параметра show sql значение false__. В любом случае вам не нужна эта информация при производстве.

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

  • Разработка конфигурации **

Конфигурация разработки должна предоставлять как можно больше полезной информации, чтобы вы могли видеть, как Hibernate взаимодействует с базой данных. Поэтому вы должны как минимум записать сгенерированные операторы SQL в конфигурацию разработки. Вы можете сделать это, активировав сообщение DEBUG для категории org.hibernate.SQL . Если вы также хотите увидеть значения параметров привязки, вам нужно установить уровень журнала org.hibernate.type.descriptor.sql равным TRACE :

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p[%c]- %m%n
log4j.rootLogger=info, stdout

# basic log level for all messages
log4j.logger.org.hibernate=info

# SQL statements and parameters
log4j.logger.org.hibernate.SQL=debug
log4j.logger.org.hibernate.type.descriptor.sql=trace

В следующем фрагменте кода показаны некоторые примеры сообщений журнала, которые Hibernate пишет с этой конфигурацией журнала. Как видите, вы получаете подробную информацию о выполненном SQL-запросе и всех установленных и извлеченных значениях параметров:

23:03:22,246 DEBUG SQL:92 - select
    order0__.id as id1__2__,
    order0__.orderNumber as orderNum2__2__,
    order0__.version as version3__2__
  from purchaseOrder order0__
  where order0__.id=1
23:03:22,254 TRACE BasicExtractor:61 - extracted value ([id1__2__]:[BIGINT]) -[1]23:03:22,261 TRACE BasicExtractor:61 - extracted value ([orderNum2__2__]:[VARCHAR]) -[order1]23:03:22,263 TRACE BasicExtractor:61 - extracted value ([version3__2__]:[INTEGER]) -[0]----

Hibernate предоставляет вам гораздо больше внутренней информации о __Session__, если вы активируете статистику Hibernate. Вы можете сделать это, установив системное свойство __hibernate.generate__statistics__ в значение true.

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

Вы можете увидеть некоторые примеры статистики в следующем фрагменте кода:

[source,text,gutter:,true]

23:04:12,123 INFO StatisticalLoggingSessionEventListener:258 - Session Metrics { 23793 nanoseconds spent acquiring 1 JDBC connections; 0 nanoseconds spent releasing 0 JDBC connections; 394686 nanoseconds spent preparing 4 JDBC statements; 2528603 nanoseconds spent executing 4 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; 9700599 nanoseconds spent executing 1 flushes (flushing a total of 9 entities and 3 collections); 42921 nanoseconds spent executing 1 partial-flushes (flushing a total of 0 entities and 0 collections) }

Я регулярно использую эту статистику в своей повседневной работе, чтобы http://www.blyts-on-java.org/hibernate-performance-tuning-part-1/[find проблемы с производительностью до того, как они возникнут в производстве], и я мог написать несколько постов только об этом. Так что давайте просто сосредоточимся на самых важных.

Строки со 2 по 5 показывают, сколько JDBC-соединений и операторов Hibernate использовало во время этого сеанса, и сколько времени он на него потратил. Вы всегда должны взглянуть на эти значения и сравнить их с вашими ожиданиями.

Если заявлений гораздо больше, чем вы ожидали, у вас, скорее всего, http://www.viousts-on-java.org/hibernate-performance-tuning-part-2/[most наиболее распространенная проблема производительности], n 1 выберите вопрос. Вы можете найти его практически во всех приложениях, и это может создать огромные проблемы с производительностью в большой базе данных. Я объясню эту проблему более подробно в следующем разделе.

Строки с 7 по 9 показывают, как Hibernate взаимодействует с кешем 2-го уровня.

Это один из http://www.viousts-on-java.org/hibernate-performance-tuning-part-3[Hibernate's 3 кэша], и он сохраняет сущности независимым от сеанса способом. Если вы используете 2-й уровень в своем приложении, вы всегда должны отслеживать эту статистику, чтобы увидеть, получает ли Hibernate сущности оттуда.

**  Конфигурация производства **

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

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

[source,text,gutter:,true]

log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.out log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p[%c]- %m%n log4j.rootLogger=info, stdout