Hibernate性能最適化【回転】


Hibernateは生まれつきの効率が低いと考える人が多く、確かに一般的にSQL文に変換するHibernateを実行する効率は直接JDBCアクセスより低いと考えられていますが、比較的良い性能最適化を経た後、Hibernateの性能はかなり満足しています.特に2次キャッシュを適用した後、キャッシュを使用しないJDBCよりも優れたパフォーマンスを得ることができます.通常のHibernateパフォーマンス最適化のポリシーについて説明します.
1.Hibernateパフォーマンス最適化のキャプチャ最適化
キャプチャとは、Hibernateがどのように関連関係をナビゲートするか、Hibernateがどのように関連オブジェクトを取得するかというポリシーです.主に、キャプチャ方法といつキャプチャするかという2つの側面を定義します.
1)どのように捕まえるか.
Hibernate 3には主に2つのキャプチャ方式があり,オブジェクト関連インスタンス(many-to-one,one-to-one)とオブジェクト関連集合(set,mapなど)に適用され,合計4つの変種である.
JOINキャプチャ:SELECT文でOUTER JOINを使用してオブジェクトの関連インスタンスまたは関連セットを取得)
SELECTキャプチャ:現在のオブジェクトの関連エンティティとセットをキャプチャするために別のSELECT文を送信します.
私の開発経験では、パフォーマンスの最適化は限られており、あまり注目する価値はありません.
例:
A.オブジェクト関連インスタンスに適用(デフォルトはfalse)
".." outer-join="true/false/auto"  .../>  

B.オブジェクト関連集合に適用(デフォルトはauto)

   ".." fetch="join/select" ... >  
   ....  
 

2)いつ捕まえるか
主に遅延ロードと即時キャプチャに分けられ、デフォルトではHibernate 3はオブジェクト関連に対して実際に遅延ロードを採用し、一般的な属性は即時キャプチャを採用し、遅延ロードと適切なキャプチャ粒度を採用することで、最適化を採用しないことに比べて性能を数倍に向上させることが多い
≪即時スナップ|Cut Immediately|oem_src≫:ホスト・オブジェクトをスナップすると、関連付けられたオブジェクトと関連付けられたセット、および属性が同時にスナップされます.
≪遅延ロード|Deferred Load|oem_src≫:ホスト・オブジェクトをキャプチャする場合、関連オブジェクトをキャプチャするのではなく、そのオブジェクトを呼び出したときにロードされます.
例:
A.オブジェクト関連インスタンスに適用(デフォルトは遅延ロード)
B.オブジェクト関連セットに適用する(デフォルトは遅延ロード)  
遅延ロードの場合、注意が必要な場合、遅延オブジェクトの使用はセッションが閉じる前に行わなければならない.HibernateのLazyInitalizationExceptionは、セッションのライフサイクル外で遅延ロードのオブジェクトが使用されているためであることが多い.Web開発を行う場合、OpenSessionInViewモードを使用し、リクエスト開始時にセッションを開き、リクエスト応答終了時にセッションを閉じることができますが、OpenSessionInViewモードを使用する場合は、応答時間が長い場合(ビジネスが複雑な場合やクライアントが低速ネットワークである場合)、セッションリソース(つまりデータベースの接続)を時間がかかりすぎると資源が尽きてしまいます
3)グリップ粒度
キャプチャ粒度とは、オブジェクトが関連関係間をナビゲートされたときに一度にロードされた数を指し、Hibernateプログラムのパフォーマンスが悪いのは、キャプチャ粒度をよく考慮していないことであり、リストをロードし、リスト内の各オブジェクトで関連付けをナビゲートすると、N+1 SQL文クエリが発生することが多い.
例:
A.オブジェクト関連インスタンスに適用(デフォルトは1)オブジェクト関連インスタンスの設定は、関連付けられたオブジェクトの上にあることに注意してください.例えば、

class User  
{  
    Group g;  
} 

では、グリップの粒度はGroupのプロファイルの上にあるはずです.

<class name="Group" table="group" batch-size="..">  
    ...  
class> 

この値には一般的な値はありません.場合によっては、関連テーブルデータが少ない場合は小さく設定できます.3-20、大きい場合は30-50に設定できます.注意すると、多ければ多いほど良いわけではありません.値が50を超えると、性能はあまり改善されませんが、無駄にメモリを消費します.
次の例があります.
List users = query.list();
20個のUserがあり、20個のUserとそのGroupを巡回している場合、batch-size(batch-size="1")を設定しないと、最悪の場合、1+20個のSQL文が必要になります.batch-size="10"を設定すると、1+2個のSQL文しか必要になりません.
B.オブジェクト関連セットに適用(デフォルトは1)

".." batch-size="" ... >  
   ....  
 

2.Hibernateパフォーマンス最適化の2次キャッシュ最適化
Hibernateのデータに対するキャッシュは2つのレベルを含む:1級キャッシュは、Sessionのレベルで行われ、主にオブジェクトキャッシュであり、そのidをキーとしてオブジェクトを保存し、Sessionの生命期間中に存在する.SessionFactoryのレベルで行われ、オブジェクトキャッシュとクエリーキャッシュがあり、クエリーキャッシュはクエリー条件をキーとしてクエリー結果を保存し、SessionFactoryのライフタイムに存在します.デフォルトでは、Hibernateは1つのキャッシュのみを有効にし、2つのキャッシュを正しく使用することで、予想外のパフォーマンスが得られることがよくあります.
1)オブジェクトキャッシュ:
1つのオブジェクトをキャプチャした後、Hiberateはidをキーとしてキャッシュし、次に同じオブジェクトをキャプチャした場合、以下の構成を使用できます.
方法1:キャッシュ・オブジェクト上での構成

<class ...>  
   "read-only/write/...." regions="group" />  
class> 

useageは、読み取り専用キャッシュ、読み書きキャッシュなど、どのようなタイプのキャッシュを使用するか(具体的にはHibernateリファレンスガイドを参照)を示すが、Hibernateの実装では読み書きキャッシュがサポートされていない部分キャッシュがあり、例えばJBossCacheはHibernateの実装では読み取り専用キャッシュにすぎず、具体的なキャッシュはキャッシュタイプのサポートを実現する場合、org.hibernate.cacheパッケージを参照
regionsはキャッシュブロックを表し、ほとんどのキャッシュインプリメンテーションはキャッシュをブロックすることが多い.この部分はオプションで、詳細は各キャッシュインプリメンテーションを参照してください.
方法2:hibernate.cfg.xmlで構成する
2つ目はもっとよく、統一的に管理できると思います.
2)クエリーキャッシュ
クエリー時にクエリー結果をクエリー条件で保存するには、次のように構成する必要があります.
A.hibernate.cfg.xmlで構成(クエリーキャッシュ有効)
"hibernate.cache.use_query_cache">true   

(前のアトリビュート名は定数を参照
org.hibernate.cfg.Enviroment.USE_QUERY_CACHE)
B.プログラム
query.setCacheable(true);  
query.setCacheRegions(...); 

クエリー・キャッシュは、クエリー結果リストのプライマリ・キー・データのみをキャッシュするため、クエリー・キャッシュとオブジェクト・キャッシュを組み合わせるとより効果的であることに注意してください.
一般的に開発では,比較的安定して頻繁に参照されるデータ,例えばデータ辞書などに対して2次キャッシュを行い,クエリ条件やクエリデータの変化が頻繁ではなくよく使用されるクエリに対して2次キャッシュを行う.2次キャッシュはメモリに格納されており、Hibernateのキャッシュは弱参照キャッシュ(WeekReference)ではないため、大きなブロックのデータを入れないように注意してください.そうしないと、メモリに大きな圧力がかかる可能性があります.
3.Hibernateパフォーマンス最適化の一括データ操作最適化
大量のデータ操作(数万から数十数百万)を行う場合は、2点に注意する必要があります.1、大量提出、2、不要な1級キャッシュデータをタイムリーに消去する必要があります.
1)一括コミットとは,セッションのflushを頻繁に使用しないことであり,flushを行うたびにHibernateはPOデータをデータベースに同期させ,マスレベルのデータ操作にとっては性能災害である(数千件のデータを同時にコミットすることと1件のデータをコミットすることでflushが一度に数十倍の差がある可能性がある).通常、データ操作はトランザクションに配置され、トランザクションがコミットされるとHibernateは自動的にflush操作を行います.
2)不要な1級キャッシュデータを即時にクリアする:Hibernateはデフォルトで1級キャッシュを採用しているため、sessionの生命期間中、すべてのデータをキャプチャした後に1級キャッシュに入れるが、データ規模が比較的大きい場合、メモリにキャプチャしたデータはメモリ圧力を非常に大きくし、一般的にバッチ操作データは一回操作された後に1級キャッシュをクリアする.例えば:
session.clear(User.class)
4.その他
dynamic-insert,dynamic-update,動的挿入と動的更新とは,Hibernateにデータを挿入させる場合は空でないデータのみを挿入し,データを修正する場合は変化するデータのみを修正すること,例えば
class User  
{  
   id  
   username  
   password  
} 

u.id=1,u.username="ayufox",u.password=nullの場合、動的挿入を設定しない場合、そのsql文はinsert into users(id,username,password)values(1,'ayufox',')であり、設定するとそのsql文はinsert into users(username)valeus('ayufox')である
以上のようにu.password='11'を変更すると、動的更新が設定されていない場合、sql文はupdate users set username='ayufox'、password='11'where id=1、設定するとupdate user set password='11'where d=1となる
設定はclassのマッピングファイルで、次のようになります.
<class name="User" table="users" dynamic=insert="true/false" dynamic-update="true/false" ...>  
class>