3 Hibernateの一般的なパフォーマンス問題とログファイルでそれらを見つける方法

1前書き

あなたはおそらくHibernateのパフォーマンスの悪さについてのいくつかの不満を読んだか、あるいはあなたがそれらのいくつかをあなた自身で苦労したことがあるでしょう。私は15年以上Hibernateを使用してきましたが、私はこれらの問題の十分以上に遭遇しました。

何年にもわたって、私はこれらの問題が避けられることとあなたがあなたのログファイルでそれらの多くを見つけることができることを学びました。この記事では、それらのうち3つを見つけて修正する方法を説明します。

2パフォーマンスの問題を見つけて修正する

2.1. 本番環境でSQL文をログに記録

最初のパフォーマンスの問題は発見するのが非常に簡単で、しばしば無視されます。

実稼働環境でのSQLステートメントのログ記録です。

いくつかのログステートメントを書くことは大したことのように思えない、そしてまさにそれをする多くのアプリケーションがそこにある。ただし、Hibernateの設定で show sql パラメータを true に設定した場合は、特に System.out.println__を介してHibernateが行うように、非常に非効率的です。

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=?

私のプロジェクトの1つでは、 show sql false__に設定することで、数分以内にパフォーマンスを20%向上させました。それは次回のスタンドアップミーティングで報告したいと思う種類の成果です🙂

このパフォーマンスの問題を解決する方法は明らかです。設定(例えばpersistence.xmlファイル)を開き、 show sql パラメータを false__に設定するだけです。とにかくあなたはこの情報を生産に必要としません。

しかし、あなたは開発中にそれらを必要とするかもしれません。そうでない場合は、2つの異なるHibernate設定を使用し(これは推奨されません)、SQLステートメントのログ記録も無効にします。そのための解決策は、ランタイム環境の特定の要件に合わせて最適化されている2つの異なるhttp://www.thoughts-on-java.org/hibernate-logging-guide/[log configuration]を開発と運用に使用することです。

  • 開発設定**

Hibernateがデータベースとどのように相互作用するのかを確認できるように、開発設定はできるだけ多くの有用な情報を提供する必要があります。したがって、少なくとも生成されたSQL文を開発設定に記録する必要があります。これを行うには、 org.hibernate.SQL カテゴリの DEBUG メッセージをアクティブにします。バインドパラメータの値も確認したい場合は、 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統計を有効にした場合、Hibernateは__Session__についてもっと多くの内部情報を提供します。これを行うには、システムプロパティ__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.thoughts-on-java.org/hibernate-performance-tuning-part-1/[本番環境で発生する前にパフォーマンスの問題を見つける]ために使用しています。それについてだけです。それでは、最も重要なものだけに注目しましょう。

2行目から5行目は、このセッション中にHibernateが使用したJDBC接続とステートメントの数、およびそれに費やした時間を示しています。あなたは常にこれらの値を見て、あなたの期待と比較するべきです。

あなたが予想よりももっと多くの声明があるなら、あなたはたぶんhttp://www.thoughts-on-java.org/hibernate-performance-tuning-part-2/[最も一般的なパフォーマンス問題]を持っています1号を選択してください。あなたはそれをほぼすべてのアプリケーションで見つけることができます、そしてそれはより大きなデータベースで巨大なパフォーマンス問題を引き起こすかもしれません。この問題については、次のセクションで詳しく説明します。

7行目から9行目は、Hibernateが2次キャッシュとどのように対話したかを示しています。

これはhttp://www.thoughts-on-java.org/hibernate-performance-tuning-part-3[Hibernateの3つのキャッシュ]のうちの1つで、エンティティをセッションに依存しない方法で格納します。アプリケーションで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