16

Hibernate 4.1.6を使用していますが、リストの作成速度に問題があります。次のクエリを実行しています。

public void doQuery(final Baz baz){
  final Query query = getSessionFactory().getCurrentSession().createQuery(
          "select c.id, foo.someValue from Foo as foo "+
          "join foo.a as a"+
          "join foo.b as b "+
          "join b.c as c "+
          "where baz=:baz"
          );
  query.setParameter("baz", baz);
  Long start=System.currentTimeMillis();
  final List<Object[]> list = query.list();
  Long end=System.currentTimeMillis();
  System.out.println((end-start));
}

データベースに送信される実際のクエリを取得するために、休止状態のデバッグをオンに設定しました。そのクエリをデータベースで直接実行したところ、0.015ミリ秒で23,000行が返されました。だから、クエリは問題ではないと思います。上記の例は、そのリストを作成するのに約32秒かかることを示しています。それをスピードアップするためにできることはありますか?

更新:休止状態のデバッグクエリを使用してcreateSQLQuery()メソッドを使用しようとしましたが、createQuery()メソッドと同じくらい遅く実行されました。

更新:ステートレスセッションを使用しようとしましたが、実行速度が遅くなりました。

更新:いくつかの統計を出力しました(hibernate.generate_statisticsフラグをtrueに設定)が、何も心配する必要はありません:

Hibernate SessionFactory Statistics [
    Number of connection requests[4]
    Number of flushes done on the session (either by client code or by hibernate[3]
    The number of completed transactions (failed and successful).[3]
    The number of transactions completed without failure[3]
    The number of sessions your code has opened.[4]
    The number of sessions your code has closed.[3]
    Total number of queries executed.[4]
    Time of the slowest query executed.[28258]
    the number of collections fetched from the DB.[6]
    The number of collections loaded from the DB.[6]
    The number of collections that were rebuilt[0]
    The number of collections that were 'deleted' batch.[0]
    The number of collections that were updated batch.[0]
    The number of your objects deleted.[0]
    The number of your objects fetched.[1]
    The number of your objects actually loaded (fully populated).[204]
    The number of your objects inserted.[1]
    The number of your object updated.[0]
]

Hibernate SessionFactory Query Statistics [
    total hits on cache by this query[0]
    total misses on cache by this query[0]
    total number of objects put into cache by this query execution[0]
    Number of times this query has been invoked[1]
    average time for invoking this query.[28258]
    maximum time incurred by query execution[28258]
    minimum time incurred by query execution[28258]
    Number of rows returned over all invocations of this query[23303]
]

更新:ネイティブクエリのScrollableResultsからnext()を実行すると、同じ速度が低下します。ループでは何もしていないことに注意してください。

    ScrollableResults results = query.scroll();
    Long start=System.currentTimeMillis();
    while (results.next()) {
       //do nothing
    }
    Long end=System.currentTimeMillis();
    System.out.println((end-start));
4

5 に答える 5

8

チューニング/最適化の問題は常に特定するのが非常に難しいため、この回答に 100% 確信があるわけではありません。

ただし、 をオンにしshow_sqlてクエリを抽出し、それをデータベースに対して直接実行し、Hibernate Query を介して 1 秒未満の結果と実行時間を確認したという事実に基づいて、Hibernate がオブジェクトを構築およびハイドレートする方法に焦点を当てています。query.list()呼び出しの結果。

Hibernate での同様のクエリ パフォーマンスの問題について言及している別のユーザーは、POJO に完全な便利なコンストラクター (各フィールドの値を受け入れるコンストラクター) を追加することで劇的なパフォーマンスの向上を確認しました

彼らはこの修正に出くわしたようですが、なぜこれが機能したのかについて明確な理解がありませんでした. Hibernate がリフレクションを使用してプロパティを検出する必要があるという憶測がありました。私自身も好奇心が強く、機会があれば Hibernate のソース コードを掘り下げて、これをよりよく理解することを計画しています。それまでの間、すべての POJO クラス属性のパラメーターを使用してこれらの完全なコンストラクターを追加し、それが違いを生むかどうかを確認することをお勧めします。

私は Hibernate のパフォーマンスの最適化に非常に興味があるので、見つけたことを教えてください。ありがとう!

于 2012-08-28T17:05:30.147 に答える
4

クエリ ( with show_sql) に問題がないように思われる場合は、コードに問題がある可能性があります。VisualVM (JDK に付属) を起動し、jvisualvmその CPU プロファイラーを使用して、どのメソッドが最も時間がかかるかを調べます。

于 2012-08-28T19:11:17.437 に答える
1

そのクエリをデータベースで直接実行したところ、0.015 ミリ秒で 23,000 行が返されました。したがって、クエリは問題ではないと推測しています。

クエリの実行時間はクエリのテキストよりも多くの要素に依存するため、これは時期尚早かもしれません。それらのクエリが同じデータに対して実行されたとしても、データベースが同じ実行計画を使用したことをどのようにして知ることができるでしょうか? ディスク キャッシュで同じ量のキャッシュ ヒットが得られることをどのように確認できますか? たとえば、hibernate はデータベースと通信するときに準備済みステートメントを使用しますが、おそらく使用していないでしょう。Oracle では、実行計画はクエリ テキストによってキャッシュされるため、別のクエリ テキストは新しく計算された実行計画を意味します。キャッシュされた実行計画は、さまざまなクエリ パラメータに基づいて形成されている可能性があるため、異なる可能性が非常に高く、実行時間が桁違いに変化する可能性があります。それがデータベースであると言っているわけではありませんが、可能性を軽視するつもりはないことに注意してください。

したがって、最初にすべきことは、データベース、または JVM で実行されている何かがその間ずっと無駄になっていないかどうかを測定することです。これを行う簡単な方法は、クエリが実行されているときに JVM の CPU 消費を監視することです。1 つのスレッドよりも大幅に少ない場合、JVM は何か (おそらくデータベース) を待機しています。

データベースの場合は、データベースの最適化ツールを使用して、実行計画とその他の関連するパフォーマンス メトリックを取得します。

JVM にある場合は、Profiler を使用してパフォーマンスのボトルネックを特定します。

于 2012-08-29T18:38:04.280 に答える
0

同様の問題に遭遇しましたが、関連しているかどうかはわかりません。基本的に、クエリごとに 1 回新しい SessionFactory を作成していたため、次のようなクエリを実行していました。

 select streamref0_.UUID as UUID145_, streamref0_.Tape_TapeId as Tape2_145_ from StreamRefToTape streamref0_ where streamref0_.UUID=?

そこに大きな数字があることに気付くでしょう。新しいセッション ファクトリごとに 1 回増加することがわかります。とにかく、これにより、オラクルは各クエリの新しい計画を実行するのにすべての時間を費やしていました(CPUはほとんどすべて「ハード解析」時間で新しい計画を生成していると報告されました-オラクルは、見たことのない計画を生成するのが遅いと思います前?)。この特定のケースでの修正は、毎回新しいファクトリではなく同じファクトリを使用することでした。Hibernate がクエリごとに異なる SQL を生成するも参照してください。

http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:2588723819082は、明らかに悪いハード解析について説明しています。

別の可能な修正は、休止状態内で「生のSQL」(jdbc)またはおそらく生のSQLクエリを使用することですが、この特定のケースでは問題を何とか解決していないようです...

于 2013-03-06T20:28:00.367 に答える