web-dev-qa-db-ja.com

単純な休止状態クエリの戻りが非常に遅い

次の休止状態クエリがあります。

Query query = session.createQuery("from MyHibernateClass");
List<MyHibernateClass> result = query.list();// executes in 7000ms

MySQLで実行されているSQLをログに記録するとき、私は見る

select 
  myhibernat0_.myFirstColumn as myfirstcolumn92_, 
  myhibernat0_.mySecondColumn as mysecondcolumn92_, 
  myhibernat0_.mythirdcolumn as mythirdcolumn92_, 
  myhibernat0_.myFourthColumn as myfourthcolumn92_ 
from MyHibernateClass myhibernat0_ 
where (1=1);

MyHibernateClassデータベーステーブルの3500行の小さなデータセットのjv​​mでJavaコードを測定する場合、約7000msかかります。

一方、次のように直接jdbcを使用する場合:

Statement statement = session.connection().createStatement();
ResultSet rs = statement.executeQuery("select * from MyHibernateClass");// 7ms
List<MyHibernateClass> result = convert(rs);// executes in 20ms

同じsqlがデータベースに入りますが、jvmのJavaコードでの時間は7ミリ秒です。

MyHibernateClassは単純なJavaゲッターとセッターを備えたBeanクラスです。この例に見られるように、特別な結果トランスフォーマーは使用しません。クラスの読み取り専用インスタンスのみが必要です。 'Hibernateセッションにアタッチする必要はありません。

休止状態バージョンを使用したいのですが、実行時間を受け入れることができません。

追加情報:休止状態ログを追加した後、表示されます

[2011-07-07 14:26:26,643]DEBUG [main] [logid: ] - 
  org.hibernate.jdbc.AbstractBatcher.logOpenResults(AbstractBatcher.Java:426) - 
  about to open ResultSet (open ResultSets: 0, globally: 0)

続いて、3500の次のログステートメント

[2011-07-07 14:26:26,649]DEBUG [main] [logid: ] - 
  org.hibernate.loader.Loader.getRow(Loader.Java:1197) - 
  result row: EntityKey[com.mycom.MyHibernateClass#1]

次のような3500個のログステートメントが続きます。

[2011-07-07 14:27:06,789]DEBUG [main] [logid: ] - 
  org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.Java:130) - 
  resolving associations for [com.mycom.MyHibernateClass#1]
[2011-07-07 14:27:06,792]DEBUG [main] [logid: ] - 
  org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.Java:226) - 
  done materializing entity [com.mycom.MyHibernateClass#1]

これは何を意味するのでしょうか?

Hibernateは最初の実装で何をしていますか、どうすればわかりますか?

24
Trym

クラスのすべての属性を持つコンストラクターを追加するのがうまくいきましたが、hibernateクエリの実行時間は70ミリ秒になりました。以前は、クラスには引数なしのデフォルトコンストラクターと、エンティティID引数を持つコンストラクターのみがありました。

28
Trym

新しい情報に基づいて、別の答えを提供する必要があると感じました。違いは、BeanのListまたはSetプロパティに1対多の関連付けが指定されているように見えます。

おそらく、遅延読み込みをオフにするlazy=falseを指定しているのでしょう。遅延読み込みをオフにすると、すべてのMyHibernateClassエンティティに関連付けられたすべてのレコードを取得します。これが、実行に時間がかかる理由です。

lazy=trueを設定してみてください。これによりはるかに高速に実行され、エンティティから明示的に要求した場合にのみ関連付けられたエンティティを取得します。

6
maple_shaft

アプリケーションでLog4jを利用する場合、Hibernate固有のさまざまなロギングオプションを設定して、Hibernateの背後で何が起こっているかをより正確に把握できます。

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/session-configuration.html#configuration-logging

私の推測では、これはアプリケーションで最初にHQLクエリを呼び出すときに発生する典型的な初期ロード時間です。後続のHQLクエリは、この最初のクエリの後、顕著かつかなり速くなるはずです。

4
maple_shaft

私はこのスレッドが古いことを知っていますが、更新すると同じ問題に遭遇しましたが、SQL Serverでは、ドライバーを使用してHibernateとSQL Sentによって印刷されるSQLが異なることがわかりました。デフォルトでMSSQLドライバーを使用すると、ドライバーはRPCが呼び出すときにストアドプロシージャとしてクエリを送信します。これは、ドライバーがMSSQL Standardsのクエリプランを最適化しようとするためです。

Hibernate Query:

select c.col1,c.col2 from customer c where c.name like @param1 and c.country like @param2

実際のドライバー送信クエリ:

@param1=somevalue, @param2=somevalue 
declar sp ....

  select c.col1,c.col2 from customer c where c.name like @param1 and c.country like @param2
go

注:DBで直接リッスンするSQLプロファイラーツールを介して取得したこのクエリ

MSSQLでのsp_execの最適化は、キャッシュされる良好なクエリプランを生成する傾向があることが判明しましたが、これにより、「パラメータースニッフィング」が発生し、この問題について詳しく知ることができます。

それを克服するために、次のオプションがありました。

  1. HQLをネイティブクエリに変更し、一部のパラメーターのオプションの再コンパイルを追加する

  2. 準備されたステートメントの代わりに直接クエリ値を使用して、パラメータ値の変換がなく、クエリがドライバによってストアドプロシージャとして変更されないようにします。

  3. ストアドプロシージャを送信しないようにドライバーの設定を変更します(MSSQLサーバーのクエリプランはこのクエリに固有になるため、これはまだ悪いことです。これはOption:2と同じですが、コードの外側にあります)

オプション1と2を使用したくなかったのは、ORMフレームワークを使用するという全体の目的がなくなったため、今のところオプション3を使用することになります

したがって、JDBC URLを変更して、オプションprepareStatement = falseを送信します。

これを設定した後、クエリが次のように送信されるというもう1つの問題がありました

 Select * from customer c where c.name like **N**'somename' and c.country=**N**'somevalue'

ここでは、コード化スキームを変換することを示す値の前にプレフィックスがあります。そのため、JDBC urlをsendUnicode = falseに無効にします。

これはすべてJTDSドライバーオプションで行いました。今までは、アプリケーションは高速で稼働しています。しばらくキャッシュするために、二次キャッシュも導入しました。

これが誰かに役立つことを願っています。何か良い提案があれば教えてください。

4
jamb

クエリの結果セットのすべての行をアプリケーションが常に使用しているという事件がありました。以下のsetFetchSizeメソッドを使用してフェッチサイズを設定することにより、速度が40倍向上することがわかりました。 (パフォーマンスの改善には、カウントクエリの追加が含まれます。)

    Long count = getStoreCount(customerId);

    Query query = session.getNamedQuery("hqlGetStoresByCustomerId")
            .setString("i_customerid",customerId)
            .setFetchSize(count.intValue());

これを行うときは注意してください。私のデータセットには約100行があり、Webリクエストの寿命に限定されていました。より大きなデータセットがある場合、Javaヒープにデータを返す前に、そのデータが存在する間Javaヒープを食べることになります。

1
gregd

私はこれが古い質問であることを知っていますが、ここに私のためにそれを修正したものがあります...

Hibernate.cfg.xmlで、正しい!DOCTYPE ...があることを確認してください。次のようになります。

<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
1
Jeffrey Hawkins