6.6. パフォーマンスチューニング
6.6.1. 他のバッチロードアルゴリズム
Hibernate では、join、select、subselect、および batch の 4 つののフェッチストラテジーのいずれかを使用して関連付けのデータを読み込むことができます。これら 4 つのストラテジーにおいて、バッチローディングでは、select フェッチの最適化ストラテジーであるため、これらのストラテジーの外で、パフォーマンスを大幅に向上させることができます。このストラテジーでは、Hibernate はプライマリーキーまたは外部キーのリストを指定して、単一の SELECT ステートメントでエンティティーインスタンスまたはコレクションのバッチを取得します。batch フェッチは、レイジー select フェッチストラテジーの最適化です。
batch フェッチの設定方法は、クラス当たりのレベルまたはコレクション当たりのレベルです。
クラス当たりのレベル
Hibernate がクラス当たりのレベルでデータを読み込む場合は、クエリーの実行時にロードする関連付けのバッチサイズが必要になります。たとえば、ランタイム時に、セッションに 30 個のインスタンスの
car
オブジェクトがロードされているとします。各car
オブジェクトはowner
オブジェクトに属します。すべてのcar
オブジェクトを繰り返し処理し、所有者を要求する場合、lazy
読み込みでは、Hibernate は所有者ごとに 30 個の select ステートメントを発行します。これはパフォーマンスのボトルネックです。代わりに、Hibernate に対して、クエリーを経由する前に所有者の次のバッチのデータを事前に読み込むように指示することもできます。
owner
オブジェクトがクエリーされると、Hibernate は同じ SELECT ステートメントで多くのこれらのオブジェクトをクエリーします。事前にクエリーする
owner
オブジェクトの数は、設定時に指定されたbatch-size
パラメーターによって異なります。<class name="owner" batch-size="10"></class>
これにより、Hibernate は、後で必要と予想される 10 個以上の
owner
オブジェクトをクエリーするようになります。ユーザーがcar A
のowner
にクエリーすると、car B
のowner
はすでにバッチロードの一部として読み込まれている可能性があります。ユーザーがデータベースに移動 (および SELECT ステートメントを実行) する代わりに、実際にcar B
のowner
が必要な場合は、現在のセッションから値を取得できます。batch-size
パラメーターに加えて、Hibernate 4.2.0 ではバッチロードのパフォーマンスを強化する新しい設定項目が導入されました。設定項目はBatch Fetchstyle
設定と呼ばれ、hibernate.batch_fetch_style
パラメーターによって指定されます。LEGACY、PADDED、DYNAMIC といった異なるバッチフェッチスタイルがサポートされています。使用するスタイルを指定するには、
org.hibernate.cfg.AvailableSettings#BATCH_FETCH_STYLE
を使用します。LEGACY: 読み込みのレガシースタイルでは、
ArrayHelper.getBatchSizes(int)
に基づく事前ビルドされたバッチサイズセットが使用されます。バッチは、既存のバッチ可能な識別子の数からの次に小さな事前ビルドされたバッチサイズを使用してロードされます。上記の例で、
batch-size
が 30 の場合、事前ビルドされたバッチサイズは [30, 15, 10, 9, 8, 7,..., 1] になります。ロード 29 識別子のバッチ処理を試みると、15、10、および 4 のバッチが発生します。対応する SQL クエリーは 3 つあり、各クエリーはデータベースから 15、10、および 4 の所有者 (owner) を読み込みます。PADDED - PADDED は、バッチローディングの LEGACY スタイルに似ています。依然として事前ビルドされたバッチサイズを使用していますが、次に大きなバッチサイズを使用し、追加の識別子プレースホルダーをパディングします。
上記の例と同様に、30 個の owner オブジェクトを初期化する場合、データベースに対してクエリーが実行されるのは 1 つのみとなります。
ただし、29 個の owner オブジェクトが初期化される場合でも、Hibernate は依然としてバッチサイズ 30 の SQL select ステートメントのみを実行し、識別子が連続する追加スペースがパディングされます。
Dynamic - バッチサイズの制限に準拠していますが、このスタイルのバッチロードは、実際に読み込まれるオブジェクト数を使用して SQL SELECT ステートメントを動的に構築します。
たとえば、owner オブジェクトが 30 個で、最大バッチサイズが 30 の場合、30 個の owner オブジェクトを取得する呼び出しは、1 つの SQL SELECT ステートメントになります。35 個を取得する呼び出しは、バッチサイズ 30 と 5 の 2 つの SQL ステートメントになります。Hibernate は、必要な数である 5 を維持するために 第 2 の SQL ステートメントを動的に変更します。また、バッチサイズは制限の 30 のままに保持します。これは、PADDED バージョンとは異なります。第 2 の SQL は PADDED されません。LEGACY スタイルとは異なり、第 2 の SQL ステートメントには固定サイズがなく、次の SQL は動的に作成されます。
クエリーが 30 個の識別子を下回る場合、このスタイルは要求された識別子の数のみを動的に読み込みます。
コレクション当たりのレベル
Hibernate では、上記の各クラス当たりのセクションにリストされているバッチフェッチサイズとスタイルを採用するロードコレクションのバッチ処理も可能です。
前のセクションで使用した例を戻すには、各
owner
オブジェクトが所有するすべてのcar
オブジェクトをロードする必要があることを考慮してください。10 個のowner
オブジェクトが現行セッションでロードされ、すべての owner で 反復すると、10 個の SELECT ステートメントが生成されます (getCars()
メソッドへの呼び出しごと)。Owner のマッピングで cars コレクションのバッチフェッチを有効にすると、Hibernate は以下のようにこれらのコレクションの事前フェッチを実行できます。<class name="Owner"><set name="cars" batch-size="5"></set></class>
そのため、バッチサイズが 10 個で、レガシーバッチスタイルを使用して つのコレクションをロードすると、Hibernate は つの SELECT ステートメントを実行し、各コレクションを取得します。
6.6.2. 変更不可のデータのオブジェクト参照の 2 次レベルのキャッシング
Hibernate は、パフォーマンスを改善するために自動的にデータをメモリーにキャッシュします。これは、特にほとんど変更されないデータに対して、必要なデータベースルックアップ回数を減らすインメモリーキャッシュによって実現されます。
Hibernate は以下の種類のキャッシュを維持します。1 次キャッシュとも呼ばれるプライマリーキャッシュは必須です。このキャッシュは現行セッションと関連付けられ、すべてのリクエストがそのセッションを通過する必要があります。セカンダリーキャッシュとも呼ばれる 2 次キャッシュは任意で、プライマリーキャッシュが参照された後にのみ参照されます。
データは、最初にデータを状態アレイにアセンブルすることで 2 次キャッシュに保存されます。このアレイはディープコピーされ、そのディープコピーがキャッシュに配置されます。その逆は、キャッシュからの読み取りに対して行われます。これは、変更 (変更不能データ) が変更できないデータには適切に機能しますが、変更できないデータには効率的ではありません。
データのディープコピーは、メモリー使用量と処理速度に関する負荷のかかる操作です。大きなデータセットでは、メモリーと処理速度がパフォーマンス制限の要因となります。Hibernate では、変更不能なデータがコピーされるのではなく、参照されるように指定できます。Hibernate は、データセット全体をコピーする代わりに、キャッシュのデータへの参照を保管できるようになりました。
これには、設定 hibernate.cache.use_reference_entries
の値を true
に変更します。デフォルトでは、hibernate.cache.use_reference_entries
は false
に設定されています。
hibernate.cache.use_reference_entries
が true
に設定されている場合は、関連付けのない変更不能なデータオブジェクトは 2 次キャッシュにコピーされず、その参照のみが保存されます。
hibernate.cache.use_reference_entries
が true
に設定されると、関連付けのある変更不能なデータオブジェクトが 2 次キャッシュにディープコピーされます。