ヘッダーをスキップ
Oracle Fusion Middleware Oracle TopLink開発者ガイド
11gリリース1(11.1.1)
B56246-01
  目次
目次
索引
索引

戻る
戻る
 
次へ
次へ
 

12TopLinkアプリケーションの最適化

TopLinkには、アプリケーションのパフォーマンスを測定し、最適化するための様々な機能が用意されています。ほとんどの機能はディスクリプタまたはセッションで有効/無効を切り替えることができるため、グローバルなパフォーマンスの向上が可能になります。

この章の内容は次のとおりです。

12.1 最適化の概要

開発サイクルのどの段階でもパフォーマンスを考慮する必要があります。これは、設計および実装作業の中でパフォーマンスの問題を認識していることを意味しますが、最初の試みで最高のパフォーマンスを達成する必要があるわけではありません。

たとえば、最適化によって設計が複雑になる場合は、最適化作業を最終的な開発フェーズに回します。それでも、後の統合が簡単になるように、最初の段階から最適化の計画を立てることは必要です。

TopLinkアプリケーションのチューニングに関連した最も重要な概念は、対話方式という考え方です。アプリケーションをチューニングする上で最も効率的なのは次の方法です。

  1. TopLinkプロファイラ(12.3項「TopLinkプロファイラを使用したTopLinkパフォーマンスの測定」を参照)またはOracle Dynamic Monitoring System(DMS)プロファイラ(12.4項「Oracle Dynamic Monitoring System(DMS)を使用したTopLinkパフォーマンスの測定」を参照)を使用して、アプリケーションのパフォーマンスを測定します。

  2. アプリケーション・コンポーネントを変更します(12.2項「アプリケーションのパフォーマンス問題の原因の識別」を参照)。

  3. パフォーマンスを再測定します。

アプリケーションのパフォーマンス向上につながる変更を特定するために、一度に変更するコンポーネントは1つまたは2つのみとします。アプリケーションをデプロイする前に、非本番環境でアプリケーションをチューニングすることも必要です。

12.2 アプリケーションのパフォーマンス問題の原因の識別

この項では、TopLinkで有効なアプリケーションの様々な部分で起こる最も一般的なパフォーマンスの問題について説明し、パフォーマンスを向上させるための提案をします。アプリケーションでパフォーマンスの問題が起こる可能性のある部分は、次のとおりです。

12.3TopLinkプロファイラを使用したTopLinkパフォーマンスの測定

パフォーマンス・チューニングにおける最も重要な課題は、最適化の対象を知ることです。アプリケーションのパフォーマンスを向上させるには、アプリケーションの中で稼働時の効率が最大に達していない領域を特定します。TopLinkパフォーマンス・プロファイラは、指定のセッション内で実行されたすべての問合せのパフォーマンス統計をロギングすることにより、パフォーマンスの問題を識別するために役立ちます。


注意:

また、Oracle JDeveloperまたはJProbeなど、一般的なパフォーマンス・プロファイラを使用して、パフォーマンスの問題を分析することも検討してください。これらのツールを使用すると、問題の正確な診断に必要な事項がより詳細にわかります。

TopLinkパフォーマンス・プロファイラでは、次の情報をTopLinkログ・ファイルに記録します(TopLinkでのロギングの概要は、87.2.6項「ロギング」を参照)。


注意:

TopLinkパフォーマンス・プロファイラは、シングルスレッドの有限個のユースケースをプロファイリングして、ボトルネックを特定する場合に使用します。

長時間実行されるマルチスレッドのサーバーの監視を可能にする場合には、TopLinkパフォーマンス・プロファイラを使用しないで、DMSプロファイラを使用してください(詳細は、12.4項「Oracle Dynamic Monitoring System(DMS)を使用したTopLinkパフォーマンスの測定」を参照)。


この項では、次の内容について説明します。

12.3.1 TopLinkパフォーマンス・プロファイラの構成方法

TopLinkパフォーマンス・プロファイラを有効にするには、セッションを構成する際に「TopLink」プロファイラ・オプションを選択します(89.6項「パフォーマンス・プロファイラの構成」を参照)。

TopLinkパフォーマンス・プロファイラは、oracle.toplink.tools.profiler.PerformanceProfilerクラスのインスタンスです。ここでは、次のパブリックAPIが用意されています。

  • logProfile: プロファイラを有効にします。

  • dontLogProfile: プロファイラを無効にします。

  • logProfileSummary: プロファイラ・ログを、操作統計などの各操作プロファイルをまとめたサマリーとして編成します。操作統計にはプロファイリングされたすべての操作の最短時間、すべての操作の合計時間、プロファイリングされた問合せによって返されたオブジェクトの数、プロファイリングされた各種操作に要した合計時間などがあります。

  • logProfileSummaryByQuery: プロファイラ・ログを、各操作プロファイルを問合せ別にまとめたサマリーとして編成します。

  • logProfileSummaryByClass: プロファイラ・ログを、各操作プロファイルをクラス別にまとめたサマリーとして編成します。

12.3.2 TopLinkプロファイラの結果へのアクセス方法

TopLinkプロファイラの結果を表示する最も簡単な方法は、テキスト・エディタを使用してTopLinkログ・ファイルを読み取る方法です。ログ・ファイルの場所などのTopLinkロギングの概要は、87.2.6項「ロギング」を参照してください。

または、TopLink Webクライアントが提供するグラフィカルなパフォーマンス・プロファイラを使用できます。詳細は、Webクライアントのオンライン・ヘルプおよびREADMEファイルを参照してください。

例12-1は、TopLinkプロファイラの出力例を示しています。

例12-1 パフォーマンス・プロファイラの出力

Begin Profile of{
ReadAllQuery(oracle.toplink.demos.employee.domain.Employee)
Profile(ReadAllQuery,# of obj=12, time=1399,sql execute=217,
prepare=495, row fetch=390, time/obj=116,obj/sec=8)
} End Profile

プロファイルの2行目には、問合せに関する次の情報が含まれています。

  • ReadAllQuery(oracle.toplink.demos.employee.domain.Employee): プロファイリングした問合せとその引数

  • Profile(ReadAllQuery): プロファイルの開始と問合せのタイプ

  • # of obj=12: 問合せに関与したオブジェクトの数

  • time=1399: 問合せの合計実行時間(ミリ秒)

  • sql execute=217: SQL文の実行に要した合計時間

  • prepare=495: SQL文の準備に要した合計時間

  • row fetch=390: データベースからの行のフェッチに要した合計時間

  • time/obj=116: 各オブジェクトで要した時間(ミリ秒)

  • obj/sec=8: 1秒当たりのオブジェクト処理数

12.4Oracle Dynamic Monitoring System(DMS)を使用したTopLinkパフォーマンスの測定

Oracle DMSは、アプリケーションおよびシステム開発者が様々なDMS Sensorを使用して、(Nounという)特定のソフトウェア・コンポーネント用にカスタマイズされたパフォーマンス・メトリックを測定し、エクスポートできるようにするライブラリです。

TopLinkでは、不可欠なオブジェクトにDMSインスツルメンテーションが組み込まれており、Java EEアプリケーションとJava EE以外のアプリケーションの両方を含む、TopLink対応のアプリケーションでランタイム・データを効率的に監視します。

DMSインスツルメンテーションは、Oracle Application Server Manageability and Diagnosabilityにおいて重要な役割を果します。TopLink固有のDMSインスツルメンテーションにより、Manageability and Diagnosabilityを使用してOracle Application ServerにデプロイされたTopLink対応のアプリケーション管理および問題の診断を簡略化できます。

TopLinkアプリケーションでDMSプロファイリングを有効にする(12.4.1項「Oracle DMSプロファイラの構成方法」を参照)ことで、アプリケーション管理のタスクとパフォーマンス・チューニングに役立つランタイム・データを収集し、このデータに簡単にアクセスすることができます。


注意:

また、Oracle JDeveloperまたはJProbeなど、一般的なパフォーマンス・プロファイラを使用して、パフォーマンスの問題を分析することも検討してください。これらのツールを使用すると、問題の正確な診断に必要な事項がより詳細にわかります。

表12-1では、SERVER-SESSION-NAMEという名前のTopLinkサーバー・セッションに対して、TopLinkがDMSを使用して提供する各種パフォーマンスおよびステータス・メトリックをリストします。TopLinkでは、ドメイン・クラス、問合せタイプ、問合せの名前(定義済の場合)および操作(定義済の場合)別に編成された問合せメトリックも提供します。

表12-2では、DMSメトリックの収集対象となる各種TopLink操作をリストします。これらの操作は、表12-1でセンサー名にOPERATION-NAMEがあるすべてのTopLinkメトリックに適用されます。

表12-3では、必要な監視情報の量に対してプロファイリング・レベルを調整する際に使用できる様々なプロファイリング・レベルをリストします。レベルはシステムのオーバーヘッド増加率の順にリストしています。

Java Management Extensions(JMX)APIをサポートする管理アプリケーション(JConsoleなど)、または任意のWebブラウザおよびDMS Spyサーブレットを使用して、実行時にDMSデータに簡単にアクセスできます。

詳細は、次を参照してください。

表12-1 TopLink DMSメトリック

DMS Noun名 Sensor名 レベル脚注1 説明

Cache_SERVER-SESSION-NAME

CacheHits

HEAVY

オブジェクトがキャッシュで見つかった回数。


CacheMisses

HEAVY

オブジェクトがキャッシュで見つからなかった回数。


Caching

ALL

キャッシュにおけるオブジェクトの追加、参照および削除に要した時間を含みます。

Connection_SERVER-SESSION-NAME

ConnectCalls

HEAVY

接続コールの合計数。


ConnectionManagement

ALL

データ・ソースからの接続、再接続、切断などの接続管理に要した時間。


ConnectionsInUse(default)

HEAVY

排他ConnectionPool(Write、ExclusiveRead)で使用中の、プール別の接続数。


ConnectionsInUse(POOL-NAME)

HEAVY

排他ConnectionPool(Write、ExclusiveRead)で使用中の、プール別の接続数。


DisconnectCalls

HEAVY

切断コールの合計数。

Miscellaneous_SERVER-SESSION-NAME

DescriptorEvents

ALL

DescriptorEventの実行に要した時間。


logging

ALL

TopLinkアクティビティのロギングに要した時間。


OPERATION-NAME

HEAVY

操作に要した合計時間: OPERATION-NAME。これは、バッチ書込みなど、どの問合せ名詞にも含まれていない特別な操作の場合です。

OPERATION-NAMEの有効な値は、表12-2「TopLink操作」を参照してください。


SessionEvents

ALL

SessionEventの実行に要した時間。


wrapping

ALL

CMP BeanおよびBMP Beanのラップに要した時間。

RCM_SERVER-SESSION-NAME

ChangesNotProcessed

ALL

オブジェクトがキャッシュで見つからなかったためにObjectChangeSetが破棄された回数。


ChangesProcessed

ALL

ObjectChangeSetがキャッシュで見つかった回数。


MessagesReceived

HEAVY

RCMを介して受信されたメッセージ数。


MessagesSent

HEAVY

RCMを介して送信されたメッセージ数。


RCMStatus

HEAVY

not configured、started、stoppedのいずれか。


RemoteChangeSets

HEAVY

リモート・マシンから受け取り、処理したチェンジ・セットの数。

Session_SERVER-SESSION-NAME

ClientSession

HEAVY

ログインしたClientSessionの数。


loginTime

NORMAL

セッションのログイン時間。


UnitOfWork

HEAVY

作成されたUnitOfWorkオブジェクトの合計数。

TopLink_SERVER-SESSION-NAME_DOMAIN-CLASS_QUERY-TYPE_QUERY-NAME

TopLink_SERVER-SESSION-NAME_DOMAIN-CLASS_QUERY-TYPE_QUERY-NAME_OPERATION-NAME

HEAVY

操作に要した合計時間: TopLink_SERVER-SESSION-NAME_DOMAIN-CLASS_QUERY-TYPE_QUERY-NAME_OPERATION-NAME

ここで、QUERY-NAMEOPERATION-NAMEはオプションです。

たとえば、次の場合

import com.acme.model.Employee;
UpdateAllQuery updateAllQuery = new UpdateAllQuery(Employee.class);
updateAllQuery.setName("UpdateAllEmployee");

DMSセンサー名は次のようになります。

TopLink_SERVER-SESSION-NAME_com.acme.model.Employee_UpdateAllQuery_UpdateAllEmployee_OPERATION-NAME

OPERATION-NAMEの有効な値は、表12-2「TopLink操作」を参照してください。

Transaction_SERVER-SESSION-NAME

deleted_object

ALL

ObjectChangeSetのアイデンティティ・マップからオブジェクトを削除する必要があります。


DistributedMerge

ALL

リモート・トランザクションの変更をローカル共有キャッシュにマージする際に要した時間。キャッシュ同期化を使用する際に表示されます。


MergeTime

ALL

変更の共有キャッシュへのマージに要した時間。


OptimisticLocks

HEAVY

スローされたオプティミスティック・ロック例外の数。


Sequencing

ALL

順序番号のメカニズムを維持し、順序番号を実際にオブジェクトに設定する際に要した時間を含みます。


TXAfterCompletion

ALL

JTS afterCompletion(Object status)メソッドに要した時間。


TXBeforeCompletion

ALL

JTS beforeCompletion()メソッドに要した時間。


UnitOfWorkCommits

HEAVY

UnitOfWorkのコミット・プロセスを測定します。


UnitOfWorkRegister

ALL

registerExistingObjectregisterNewContainerBeanregisterNewContainerBeanForCMPregisterNewObjectregisterObjectおよびreadIntoWorkingCopyで要した時間を含みます。


UnitOfWorkRollbacks

HEAVY

ロールバックされたUnitOfWorkコミットの数。


脚注1 各レベル設定の詳細は、表12-3を参照してください。

表12-2 TopLink操作

操作 説明

DatabaseExecution

JDBC文へのコールに要した時間。closeexecuteUpdateおよびexecuteQueryへのコールに要した時間も含みます。

EISExecuteTime

データ・ソースでの実行からのInteractionSpecおよびDatabaseRowオブジェクトの作成に要した時間。EISに固有です。

ObjectBuilding

データベース行から永続オブジェクトの作成に要した時間。

QueryPreparation

問合せの準備に要した時間。SQLの準備に要した時間を除きます。

RowFetch

JDBCのResultSetからのDatabaseRowオブジェクトの作成に要した時間。標準のSQLコールおよびストアド・プロシージャ・コールを含みます。

SqlGeneration

SQLの生成に要した時間。TopLinkの式の場合は、式のSQLへの変換に要した時間。

SqlPrepare

オブジェクト・リレーショナル: JDBCで文の準備に要した時間。

EIS: 接続に関連するインタラクションを作成し、入力および出力レコード・オブジェクトを作成するEISで要した時間。


表12-3 DMSメトリック収集レベル

レベル 説明

NONE

すべてのDMSメトリック収集を無効にする。

NORMAL

TopLink DMSメトリック収集を有効にする。非常に小さいオーバーヘッドを追加します。これは、デフォルト設定です。

HEAVY

基本的なTopLink DMSメトリック収集を有効にする。約1%のオーバーヘッドを追加します。

ALL

すべてのTopLink DMSメトリックを有効にする。約3%のオーバーヘッドを追加します。


12.4.1 Oracle DMSプロファイラの構成方法

OC4J以外のアプリケーション・サーバーにデプロイされたTopLink Java EEアプリケーション用またはTopLink Java SEアプリケーション用のDMSメトリック収集を有効にするには、次の手順を実行します。

  1. dms.jarファイルがアプリケーションのクラスパスにあることを確認します。

    デフォルトでは、dms.jarファイルは<ORACLE_HOME>\jlibディレクトリにあります。

  2. システム・プロパティoracle.dms.sensors=<level>を設定します。<level>には、表12-3にリストした値のいずれかが適用されます。

  3. DMSプロファイラを有効にするには、TopLinkセッションを構成する際に「DMS」プロファイラ・オプションを選択します(89.6項「パフォーマンス・プロファイラの構成」を参照)。

Oracle WebLogic ServerにデプロイされたEclipseLink JPAアプリケーション用のDMSメトリック収集を有効にするには、次のように、persistence.xmlファイルでeclipselink.profiler永続性単位プロパティをDMSPerformanceProfilerに設定します。

<property name="eclipselink.profiler" value="DMSPerformanceProfiler"/>

詳細は、『EclipseLink Developer's Guide』の「How to Use the Persistence Unit Properties for Optimization」(http://wiki.eclipse.org/Using_EclipseLink_JPA_Extensions_%28ELUG%29#How_to_Use_the_Persistence_Unit_Properties_for_Optimization)を参照してください。

次のアプリケーションのタイプに応じて、TopLinkアプリケーションにDMSサポートを構成します。

12.4.1.1 OC4JのTopLink CMPアプリケーションにおけるOracle DMSプロファイラの構成

デフォルトでは、DMSメトリック収集は、OC4JにデプロイされたTopLink CMPアプリケーションで有効です。OC4JにデプロイされたBMPアプリケーションまたはPOJOアプリケーションの場合は、DMSメトリック収集を構成する必要があります(89.6項「パフォーマンス・プロファイラの構成」を参照)。

OC4JにデプロイされたTopLink EJBでは、OC4Jコマンドライン・プロパティ-Doracle.dms.sensors=<level>で指定されたDMS構成が必要になります。<level>には、表12-3にリストされた値のいずれかが適用されます。

12.4.1.2 OC4JのEclipseLink JPAアプリケーションにおけるOracle DMSプロファイラの構成

OC4JにデプロイされたEclipseLink JPAアプリケーション用のDMSメトリック収集を有効にするには、次のように、persistence.xmlファイルでeclipselink.profiler永続性単位プロパティをDMSPerformanceProfilerに設定します。

<property name="eclipselink.profiler" value="DMSPerformanceProfiler"/>

詳細は、『EclipseLink Developer's Guide』の「How to Use the Persistence Unit Properties for Optimization」(http://wiki.eclipse.org/Using_EclipseLink_JPA_Extensions_%28ELUG%29#How_to_Use_the_Persistence_Unit_Properties_for_Optimization)を参照してください。

12.4.2 JMXを使用したOracle DMSプロファイラ・データへのアクセス方法

Java Management Extensions(JMX)APIを使用すると、DMSプロファイラのランタイム・データを、EJBに類似したMBeanコンポーネントを介して、管理されているアプリケーション(TopLink)からJMX準拠の管理アプリケーションにパブリッシュできます。

ランタイム・サービスを有効にするようTopLinkアプリケーションを構成し(89.6項「パフォーマンス・プロファイラの構成」を参照)、アプリケーションをOracle WebLogic Serverにデプロイする場合、TopLinkランタイムでJMX MBeanをデプロイするため、JMX管理アプリケーションから、アプリケーションでパブリッシュするDMSプロファイラ・ランタイム・データにアクセスできます。

Oracle WebLogic ServerでのJMXの使用の詳細は、8.3.3項「JMXの統合方法」を参照してください。

12.4.3 DMS Spyサーブレットを使用したOracle DMSプロファイラ・データへのアクセス方法

DMS対応のTopLinkアプリケーションが稼働し始めると、収集されているDMSデータにアクセスできるようになります。

DMS Spyサーブレットは、DMSを使用するすべてのJavaプロセスで使用可能です。これを使用すると、Webブラウザの単一のJavaプロセス用のメトリックを監視できます。

DMS Spyサーブレットを使用して直接DMSデータにアクセスするには、次の手順を実行します。

  1. dms.jarファイルがアプリケーションのクラスパスにあることを確認します。

    デフォルトでは、dms.jarファイルは<ORACLE_HOME>\jlibにあります。

  2. 監視するJavaプロセスを有効にしたDMSに、次のシステム・プロパティを設定します。

    oracle.dms.publisher.classes=oracle.dms.http.Httpd
    oracle.dms.httpd.port.start=<port>
    

    <port>は、DMSがリクエストを受け入れるHTTPポートです(デフォルト値は46080)。

  3. 監視するJavaプロセスを再起動して、システム・プロパティの変更を適用します。

  4. Webブラウザで次のURLを入力してJavaプロセスに接続し、Spyサーブレットにアクセスします。

    http://<host>:<port>/dms0/Spy
    

    <host>はJavaプロセスのホスト名で、<port>oracle.dms.httpd.port.startシステム・プロパティによって指定された値です。

    Spyサーブレットでは、現在のDMSのレベル設定に該当するTopLink DMS対応のオブジェクトをすべて表示します。図12-1は、DMS Spyサーブレットの表示例を示したものです。

    図12-1 DMS Spyサーブレットの表示

    図12-1の説明が続きます
    「図12-1 DMS Spyサーブレットの表示」の説明

12.5 一般的なパフォーマンスの最適化の識別

一般に、アプリケーションで必要な場合を除き、TopLinkのデフォルト動作をオーバーライドしないでください。ただし、TopLinkのデフォルト設定の中には、開発環境に適したものがあります。これらの設定は、本番環境に応じて変更してください(12.6項「本番環境用の最適化」を参照)。

手動でコーディングするのではなく、Oracle JDeveloper TopLinkエディタまたはTopLink Workbenchを使用します。これらのツールは使用しやすいだけでなく、デプロイXML(および必要に応じて生成されたコード)にエクスポートされるデフォルト構成は、ほとんどのアプリケーションに対して最適化されたベスト・プラクティスを示します。

12.6 本番環境用の最適化

TopLinkのデフォルト設定の中には、開発環境に適したものがあります。これらの設定は、本番環境に応じて変更してパフォーマンスを最適化することをお薦めします。該当するデフォルト設定は次のとおりです。

12.7 スキーマの最適化

最適化は、データベース・スキーマとオブジェクト・モデルを設計する際の重要な考慮事項です。パフォーマンスに関する問題のほとんどは、オブジェクト・モデルまたはデータベース・スキーマが複雑すぎる場合に発生します。その結果、データベース処理が遅くなり、問合せが困難になることがあります。このような状況が生まれる可能性が最も高いのは、データベース・スキーマを複雑なオブジェクト・モデルから直接導出した場合です。

パフォーマンスを最適化するには、オブジェクト・モデルおよびデータベース・スキーマを一緒に設計します。ただし、各モデルはそれぞれ最適に設計できます。つまり、この2つの間には1対1の直接的な相関関係を必要としません。

この項では、次のスキーマの最適化の例を示します。

12.7.1 スキーマ例1: 2つの表を1つの表に集約

スキーマを最適化する一般的な方法は、2つの表を1つの表に集約することです。そうすると、必要なデータベース操作が2つではなく1つのみになるため、読取りと書込みのパフォーマンスが向上します。

表12-4表12-5は、表の集約方法を示しています。

表12-4 元のスキーマ(2つの表の集約例)

要素 詳細

タイトル

ACME Member Location Tracking System

クラス

Member、Address

MEMBER、ADDRESS

リレーションシップ

ソース: Member、インスタンス変数: address、マッピング: 1対1、ターゲット: Address


このアプリケーションの特性から、必ず従業員と住所を一緒に検索する必要があります。結果として、住所情報に基づいてメンバーを問い合せるにはデータベースの結合が必要であり、メンバーとその住所を読み取るには2つの読取り文が必要です。メンバーを書き込むには、2つの書込み文が必要です。そのため、システムが不必要に複雑になり、パフォーマンスが低下します。

よりよい解決策は、MEMBER表とADDRESS表を1つの表に結合し、1対1リレーションシップを集約リレーションシップに変更することです。そうすると、すべての情報を1回の操作で読み取ることができ、1つの表で1行のみ変更すればよくなるため、更新と挿入のスピードが倍増します。

表12-5 最適化されたスキーマ(2つの表の集約例)

要素 詳細

クラス

Member、Address

MEMBER

リレーションシップ

ソース: Member、インスタンス変数: address、マッピング: 集約、ターゲット: Address


12.7.2 スキーマ例2: 1つの表を多数の表に分割

システムの全体的なパフォーマンスを向上させるには、大きな表を複数の小さな表に分割します。そうすることで、データベースの問合せに必要なデータ通信量が大幅に減少します。

たとえば、表12-6に示すシステムでは、従業員が組織内のプロジェクトに割り当てられます。最も一般的な操作は、従業員とプロジェクトのセットを読み取り、従業員をプロジェクトに割り当てて、従業員を更新するというものです。従業員の住所または職務分類も、従業員が配属されるプロジェクトの特定に使用されることがあります。

表12-6 元のスキーマ(1つの表を多数の表に分割する例)

要素 詳細 インスタンス変数 マッピング ターゲット

タイトル

ACME Employee Workflow System




クラス

Employee、Address、PhoneNumber、EmailAddress、JobClassification、Project




EMPLOYEE、PROJECT、PROJ_EMP




リレーションシップ

Employee

address

集約

Address


Employee

phoneNumber

集約

EmailAddress


Employee

emailAddress

集約

EmailAddress


Employee

job

集約

JobClassification


Employee

projects

多対多

Project


データベースから大量の従業員のレコードを読み取る場合、その集約部分も読み取る必要があります。そのため、システムで一般的な読取りパフォーマンスの問題が発生します。この問題を解決するには、表12-7に示すように、EMPLOYEE表をEMPLOYEE、ADDRESS、PHONE、EMAIL、JOBの各表に分割します。

通常読み取るのは従業員情報のみであるため、表を分割するとデータベースからクライアントに送信されるデータの量が減少します。その結果、データ通信量が25%減少し、読取りのパフォーマンスが向上します。

表12-7 最適化されたスキーマ(1つの表を多数の表に分割する例)

要素 詳細 インスタンス変数 マッピング ターゲット

タイトル

ACME Employee Workflow System




クラス

Employee、Address、PhoneNumber、EmailAddress、JobClassification、Project




EMPLOYEE、ADDRESS、PHONE、EMAIL、JOB、PROJECT、PROJ_EMP




リレーションシップ

Employee

address

1対1

Address


Employee

phoneNumber

1対1

EmailAddress


Employee

emailAddress

1対1

EmailAddress


Employee

job

1対1

JobClassification


Employee

projects

多対多

Project


12.7.3 スキーマ例3: 階層をまとめる

オブジェクト指向の設計をリレーショナル・モデルに変換する際によくある間違いは、表の大規模な階層をデータベース上に作成することです。その結果、このタイプの設計に対する問合せは多数の結合を必要とすることがあるため、問合せが困難になります。通常は、継承階層内のいくつかのレベルを1つの表にまとめるのが有効です。

表12-8は、会社の営業担当者にクライアントを割り当てるシステムを示しています。マネージャは、直属の部下である営業担当者の追跡も行います。

表12-8 元のスキーマ(階層をまとめる例)

要素 詳細

タイトル

クラス

Person

PERSON

Employee

PERSON、EMPLOYEE

SalesRep

PERSON、EMPLOYEE、REP

Staff

PERSON、EMPLOYEE、STAFF

Client

PERSON、CLIENT

Contact

PERSON、CONTACT


このシステムでは、システム開発やパフォーマンスの障害となる複雑さの問題が発生しています。データベースに対する問合せのほとんどすべてで、リソースに負荷のかかる大規模な結合が必要です。表12-9に示すように、3つのレベルからなる表の階層を1つの表にまとめれば、システムの複雑さが大幅に低減します。システムから結合が排除され、問合せが単純化されます。

表12-9 最適化されたスキーマ(階層をまとめる例)

要素 詳細

クラス

Person

none

Employee

EMPLOYEE

SalesRep

EMPLOYEE

Staff

EMPLOYEE

Client

CLIENT

Contact

CLIENT


12.7.4 スキーマ例4: 多数から1つを選択

1対多リレーションシップでは、1つのソース・オブジェクトが他のオブジェクトのコレクションを所有します。ソース・オブジェクトがコレクション内の特定のオブジェクト1つを頻繁に必要とするが、他のオブジェクトはまれにしか必要としないという場合もあります。このようなケースでは、頻繁に必要とされるオブジェクトのインスタンス変数を追加することで、返される結果セットのサイズを小さくすることができます。その結果、コレクション内の他のオブジェクトをインスタンス化せずにオブジェクトにアクセスすることが可能になります。

表12-10は、国際的な船会社が輸送中の荷物の位置を追跡するためのシステムを示しています。荷物がある位置から別の位置に移動すると、データベースではその荷物に対して新しい位置エントリが作成されます。特定の荷物に対する最も一般的な問合せは、現在位置についてのものです。

表12-10 元のスキーマ(多数から1つを選択する例)

要素 詳細 インスタンス変数 マッピング ターゲット

タイトル

ACME Shipping Package Location Tracking System




クラス

Package、Location




PACKAGE、LOCATION




リレーションシップ

Package

locations

1対多

Location


このシステムでは、荷物が目的地に着くまでの間に、荷物のLOCATIONコレクションにいくつかの位置の値が蓄積される可能性があります。データベースからすべての位置を読み取ると、現在位置のみが必要である場合は特に、リソースに負荷がかかります。

このタイプの問題を解決するには、現在位置を表すインスタンス変数を追加します。その後、インスタンス変数の1対1マッピングを追加し、インスタンス変数を使用して現在位置を問い合せます。表12-10のように荷物に関連付けられているすべての位置を読み取らずに現在位置を問合せできるようになるため、システムのパフォーマンスが大幅に向上します。

表12-11 最適化されたスキーマ(多数から1つを選択する例)

要素 詳細 インスタンス変数 マッピング ターゲット

クラス

Package、Location




PACKAGE、LOCATION




リレーションシップ

Package

locations

1対多

Location


Package

currentLocation

1対多

Location


12.8 マッピングとディスクリプタの最適化

常にインダイレクション(遅延ロード)を使用します。インダイレクションは、最適化するデータベース・アクセスでクリティカルであるだけでなく、これによってキャッシュ・アクセスおよび作業ユニットの処理の最適化など、TopLinkでその他複数の最適化を実行できます。121.3項「インダイレクション(遅延ロード)の構成」を参照してください。

アプリケーションで必要にならないかぎり、存在チェック・オプションcheckCacheThenDatabaseをディスクリプタで使用しないようにします(119.17項「ディスクリプタ・レベルでのキャッシュ存在チェックの構成」を参照)。デフォルトの存在チェック動作の方がパフォーマンスに優れています。

TopLinkでオブジェクトのインスタンス化に使用するデフォルト・コンストラクタで、初期化に高い負荷をかけないようにします。そのかわりに、遅延初期化またはTopLinkのインスタンス化ポリシー(119.28項「インスタンス化ポリシーの構成」を参照)を使用してディスクリプタを構成し、別のコンストラクタを使用します。

TopLinkのマッピングでメソッド・アクセスを使用しないようにします(121.6項「マッピング・レベルでのメソッドまたは直接フィールド・アクセスの構成」を参照)。特に、getメソッドまたはsetメソッドに負荷の高い、または副次的な危険性のあるコードが存在する場合は、メソッド・アクセスのかわりにデフォルトの属性アクセスを直接使用してください。

12.9 セッションの最適化

サーバー環境ではデータベース・セッションではなく、サーバー・セッションを使用します。

リモート・セッションではなく、TopLinkのクライアント・セッションを使用します。クライアント・セッションは、ほとんどのマルチユーザーJava EEアプリケーション・サーバー環境に適しています。

クライアント・セッションをプールしないでください。セッションをプールしても、パフォーマンスは向上しません。

セッションの読取り/書込み接続プールのサイズを同時スレッド数に適した値に増やすことをお薦めします(50など)。この構成は、内部接続プールの使用時はTopLinkで、外部接続プールの使用時はデータ・ソースで行います。

詳細は、次を参照してください。

12.10 キャッシュの最適化

キャッシュ・コーディネーション(102.3項「キャッシュ・コーディネーション」を参照)は、分散される可能性のある複数のセッションのインスタンスが相互にオブジェクト変更を送信できるようにする方法の1つで、これにより、各セッションのキャッシュは最新の状態を保つことができます。

ただし、キャッシュ・コーディネーションは、ある特性を持つアプリケーションに最も適しています(102.3.1項「キャッシュ・コーディネーションの使用が必要な場合」を参照)。キャッシュ・コーディネーションを実装する前に、オブジェクト・アイデンティティのタイプ(119.12項「ディスクリプタ・レベルでのキャッシュ・タイプとサイズの構成」を参照)、キャッシュの無効化(102.2.5項「キャッシュの無効化」を参照)、またはキャッシュの独立性(102.2.7項「キャッシュの分離」を参照)などの代替手段を使用して、各クラスについてTopLinkのキャッシュを調整します。キャッシュを調整することにより、各クラスのタイプに最適なキャッシュ構成を実現でき(表12-12を参照)、分散キャッシュ・コーディネーションの必要性を完全になくすことができます。

表12-12 クラス・タイプ別アイデンティティ・マップおよびキャッシュ構成

クラス・タイプ アイデンティティ・マップ・オプション キャッシュ・オプション

読取り専用

ソフト、ハード、またはフル脚注1


大部分が読取り

ソフトまたはハード

キャッシュ無効化またはキャッシュ・コーディネーション

大部分が書込み

弱参照キャッシュ

キャッシュ無効化


脚注1 インスタンス数に制限がある場合。

キャッシュ・コーディネーションを使用する場合は、RMIではなく、キャッシュ・コーディネーション用のJMSを使用してください。JMSは、より堅牢で構成しやすく、非同期で動作します。同期方式のキャッシュ・コーディネーションが必要な場合は、RMIを使用します。

このオブジェクト・タイプのインスタンスが要求された場合、ディスクリプタを構成して、TopLinkランタイムでセッション・キャッシュがリフレッシュされるタイミングを制御できます(119.9項「キャッシュ・リフレッシュ機能の構成」を参照)。「常にリフレッシュする」または「キャッシュ・ヒットを無効にする」の使用はお薦めしません。

「常にリフレッシュする」を使用すると、必要ないときや希望しないときに、問合せのキャッシュをリフレッシュすることがあります。そのかわりに、問合せ単位のキャッシュ・リフレッシュを構成することを検討してください(108.16.5項「キャッシュのリフレッシュ方法」を参照)。

「キャッシュ・ヒットを無効にする」を使用すると、TopLinkでは、主キーに基づくオブジェクトの読取り問合せのキャッシュが省略されます。これにより、このオブジェクト・タイプで主キーに基づくオブジェクトの読取り問合せが実行されるたびに、データベース・ラウンドトリップが起こり、キャッシュのパフォーマンスが低下します。「常にリフレッシュする」とともに使用した場合は、このオプションによって、問合せがすべてデータベースにアクセスします。これは、パフォーマンスに大きな影響を与えることがあります。これらのオプションは、特別な状況でのみ使用します。

12.11データ・アクセスの最適化

TopLinkでは、自分のアプリケーションからアクセスするデータ・ソースのタイプによって、低レベルのデータ読取りと書込みのパフォーマンス調整に使用できる、様々なLoginオプションを提供します。

いくつかのテクニックを使用して、アプリケーションのデータ・アクセス・パフォーマンスを向上させることができます。この項では、より一般的な次のようなアプローチについて説明します。

12.11.1 JDBCドライバ・プロパティの最適化方法

自分のアプリケーション用に選択するJDBCドライバのデフォルトの動作を検討します。JDBCドライバ・オプションには、データ・アクセス・パフォーマンスに影響するものがあります。

重要なJDBCドライバ・プロパティには、Oracle JDeveloper TopLinkエディタ、TopLink WorkbenchまたはTopLink APIを直接使用して構成できるものがあります(たとえば、12.12.6項「JDBCフェッチ・サイズを使用した最適化方法」を参照)。

Oracle JDeveloper TopLinkエディタ、TopLink WorkbenchまたはTopLink APIで直接サポートしていないJDBCドライバ・プロパティでも、TopLinkがJDBCドライバに渡す汎用JDBCプロパティとして構成できます。

たとえば、Sybase JConnectなどの一部のJDBCドライバでは、データベース・ラウンドトリップを実行し、接続が切断されているかどうかをテストします。つまり、JDBCドライバ・メソッドisClosedをコールすると、ストアド・プロシージャ・コールまたはSQLの選択を実行したことになります。このデータベース・ラウンドトリップにより、パフォーマンスが大幅に低下します。これを避けるには、この動作を無効にできます。Sybase JConnectの場合は、プロパティ名CLOSED_TESTを値INTERNALに設定できます。

自分のTopLinkアプリケーション内から汎用JDBCドライバ・プロパティを構成する詳細は、97.5項「プロパティの構成」を参照してください。

12.11.2 データ形式の最適化方法

デフォルトでは、TopLinkは、アプリケーションが必要とする形式でJDBCからデータにアクセスして、データ・アクセスを最適化します。たとえば、TopLinkでは、後でlongに変換する必要があるようなBigDecimalをドライバから取得するかわりにlongデータ・タイプをJDBCから取得します。

古いJDBCドライバには、データを正確に変換せず、この最適化と競合するものがあります。このような場合、この最適化を無効にできます(98.7項「詳細オプションの構成」を参照)。

12.11.3 バッチ書込みを使用した最適化方法

バッチ書込みにより、INSERTUPDATEおよびDELETE文のグループを、個別にではなく1つのトランザクションでデータベースに送信して、データベース・パフォーマンスを向上させることができます。

パラメータ使用のSQLを使用せずに行った場合、これを動的バッチ書込みといいます。

パラメータ使用のSQLとともに行った場合(12.11.5項「パラメータ使用のSQL(パラメータ・バインド)とプリコンパイルされたSQL文のキャッシュを使用した最適化方法」を参照)、これをパラメータ使用のバッチ書込みといいます。これにより、同じタイプの挿入などの文のグループを繰り返し実行できます。これは、1つの文として、またパラメータのバインド・セットとして実行されます。これにより、データベースがバッチを解析する必要がなくなり、パフォーマンスが大幅に向上します。

バッチ書込みを使用する場合、バッチ書込みの最大サイズを調整できます。

JPAアプリケーションでは、永続性単位プロパティeclipselink.jdbc.batch-writingを使用できます(『EclipseLink Developer's Guide』の「EclipseLink JPA Persistence Unit Properties for JDBC Connection Communication」の表(http://wiki.eclipse.org/Using_EclipseLink_JPA_Extensions_%28ELUG%29#How_to_Use_EclipseLink_JPA_Extensions_for_JDBC_Connection_Communication)を参照)。

CMPまたはPOJOアプリケーションでは、LoginインタフェースのsetMaxBatchWritingSizeメソッドを使用できます。この値の持つ意味は、パラメータ使用のSQLを使用しているかどうかによって変わります。

  • パラメータ使用のSQLを使用している(bindAllParametersメソッドをコールすることでLoginを構成する)場合は、バッチ書込みの最大サイズは、バッチ処理する文の数であり、デフォルトは100です。

  • 動的SQLを使用している場合は、バッチ書込みの最大サイズは、文字単位のSQL文字列バッファのサイズであり、デフォルトは32000です。

すべてのデータベースおよびJDBCドライバがTopLinkをサポートするとはかぎらないため、デフォルトでは、TopLinkでバッチ書込みを有効にしません。このオプションをサポートするデータベースおよびJDBCドライバを選択した場合は、バッチ書込みを有効にすることをお薦めします。JDBCドライバでバッチ書込みをサポートしていない場合は、TopLinkバッチ書込みと呼ばれるTopLinkのバッチ書込み機能を使用します(98.6項「JDBCオプションの構成」を参照)。

バッチ書込みを使用して書込み問合せを最適化する例の詳細は、12.12.10.1.2項「バッチ書込みおよびパラメータ使用のSQL」を参照してください。

12.11.4 外部結合読取りと継承先サブクラスの使用方法

例12-2に示すように、継承先サブクラスを外部結合できるようにオブジェクト・レベルの読取り問合せを構成すると、クラスごとに問合せをする負荷を回避できます。

例12-2 ObjectLevelReadQueryの構成による継承先サブクラスの外部結合

objectLevelReadQuery.setShouldOuterJoinSubclasses(true);

例12-3に示すようにディスクリプタのInheritancePolicyを構成しても、同じことを実行できます。InheritancePolicyを構成すると、このオプションはディスクリプタのクラスのすべての問合せに適用されます。

例12-3 ディスクリプタの構成で可能になる継承先サブクラスの外部結合

myDescriptor.getInheritancePolicy().setShouldOuterJoinSubclasses(true);

詳細は、次を参照してください。

12.11.5パラメータ使用のSQL(パラメータ・バインド)とプリコンパイルされたSQL文のキャッシュを使用した最適化方法

パラメータ使用のSQLを使用すると、JDBCドライバまたはデータベース・サーバーで決められた文の長さ制限を超えないように、SQL問合せの全体の長さを維持できます。

パラメータ使用のSQLおよびプリコンパイルされたSQL文のキャッシュを使用すると、頻繁にコールされる問合せのSQLをデータベースSQLエンジンで解析およびプリコンパイルする回数が減り、パフォーマンスが向上します。

デフォルトでは、TopLinkはパラメータ使用のSQLは有効にしますが、プリコンパイルされたSQL文のキャッシュは有効にしません。文のキャッシュは、内部接続プールの使用時はTopLinkで、外部接続プールの使用時はデータ・ソースで有効にし、アプリケーションに適した文のキャッシュ・サイズを選択することをお薦めします。


注意:

パラメータ・バインドが有効な場合は、固定長のCHAR型であるデータベース・フィールドに問合せを実行すると、結果が返されないことがあります。空白がトリミングされない可能性があるからです。かわりに、次を実行できます。
  1. 可変長の列タイプを使用します(たとえば、VARCHAR)。

  2. 正しいパディングを手動で強制的に適用します(アプリケーション内またはコンバータ内)。

  3. パラメータ・バインドを使用しません。


すべてのJDBCドライバがすべてのJDBCバインド・オプションをサポートするとはかぎりません(98.6項「JDBCオプションの構成」を参照)。選択するオプションの組合せにより、ドライバの動作が異なる場合があります。JDBCオプションを選択する前に、使用するJDBCドライバのドキュメントを参照してください。バインド・オプションを選択する際に、次の方法を検討してください。

  1. すべてのパラメータを、他のすべての無効なバインド・オプションとバインドします。

  2. ある大きなパラメータのバインドに失敗した場合は、パラメータのデータ・タイプ、およびJDBCドライバでサポートするバインド・オプションに応じて、次のいずれかのオプションを有効にします。

    1. 大きなStringパラメータをバインドするには、「文字列バインディング」を有効にします。

      それでも大きなStringパラメータをバインドできない場合は、最大Stringサイズを調整します。TopLinkでは、デフォルトで最大Stringサイズを32000文字に設定します。

    2. 大きなByte配列パラメータをバインドするには、「バイト配列バインディング」を有効にします。

  3. ある大きなパラメータのバインドに失敗した場合は、「バインド用ストリーム」を有効にします。

    通常は、文字列バインディングまたはバイト配列バインディングを構成すると、バインド用ストリームが起動します。起動しない場合は、明示的にバインド用ストリームを構成すると起動します。

TopLink外部接続プールを使用するJava EEアプリケーションの場合、TopLinkにパラメータ使用のSQLを構成する必要がありますが、プリコンパイルされたSQL文のキャッシュは構成できません。この場合、プリコンパイルされたSQL文のキャッシュをアプリケーション・サーバー接続プールに構成する必要があります。たとえば、OC4Jでは、(connection-driveroracle.jdbc.OracleDriverclassoracle.j2ee.sql.DriverManagerDataSourceの)管理されたdata-sourceを使用してdata-source.xmlファイルを構成する場合、JDBC文のキャッシュを有効にし、キャッシュされる文の最大数を定義する、ゼロ以外のnum-cached-statementsを構成できます。

TopLink内部接続プールで使用するアプリケーションの場合は、パラメータ使用のSQLおよびプリコンパイルされたSQL文のキャッシュを構成できます。

パラメータ使用のSQLとプリコンパイルされたSQL文のキャッシュは、次のレベルに構成できます。

12.12 問合せの最適化

TopLinkでは、データの読取り、書込みおよび更新用の問合せAPIを幅広く提供しています。この項では、様々な状況における問合せのパフォーマンスの最適化の方法について説明します。

問合せを最適化する前に、12.11項「データ・アクセスの最適化」で最適化の提案について検討してください。

この項では、次の内容について説明します。

12.12.1 パラメータ使用のSQLとプリコンパイルされたSQL文のキャッシュを使用した最適化方法

これらの機能を使用すると、問合せが再実行された際に、事前に解析された問合せデータベース文をキャッシュおよび再利用できます。

詳細は、12.11.5項「パラメータ使用のSQL(パラメータ・バインド)とプリコンパイルされたSQL文のキャッシュを使用した最適化方法」を参照してください。

12.12.2 名前付き問合せを使用した最適化方法

可能な場合は常にアプリケーションで名前付き問合せを使用します。名前付き問合せは重複の防止に役立ち、メンテナンスと再使用が容易です。また、名前付き問合せを使用すると、複雑な問合せの動作をアプリケーションに簡単に追加できます。また、名前付き問合せを使用すると、1回のみ問合せを作成でき、SQL生成をキャッシュすることもできます。

詳細は、108.8項「名前付き問合せ」を参照してください。

12.12.3 バッチ読取りと結合読取りを使用した最適化方法

データベースの読取り操作を最適化するために、TopLinkではバッチ読取りと結合読取りの両方がサポートされています。これらの手法を使用すると、特に結果セットに多数のオブジェクトが含まれている場合は、読取り操作中にデータベースにアクセスする回数が大幅に減少します。

詳細は、次を参照してください。

12.12.4 部分オブジェクト問合せとフェッチ・グループを使用した最適化方法

部分オブジェクト問合せを使用すると、完全なオブジェクトではなく、部分的にデータが移入されたオブジェクトをデータベースから取得できます。

JPAまたはPOJOによるウィービング、あるいはCMPアプリケーションを使用している場合、フェッチ・グループを使用して同じパフォーマンスの最適化を実現できます。

部分オブジェクト読取りの詳細は、108.7.1.3項「部分オブジェクト問合せ」を参照してください。

フェッチ・グループの詳細は、16.2.4項「フェッチ・グループ」を参照してください。

12.12.5 読取り専用問合せを使用した最適化方法

例12-4に示すように、オブジェクト・レベルの読取り問合せを読取り専用として構成できます。このような問合せをUnitOfWork(またはEclipseLink JPA永続性プロバイダ)のコンテキストで実行すると、読取り専用の未登録オブジェクトがTopLinkによって返されます。読取り専用オブジェクトは登録や変更の確認をする必要がないため、この方法で読取り専用データを問い合せると、パフォーマンスを向上できます。

例12-4 読取り専用としてのObjectLevelReadQueryの構成

objectLevelReadQuery.setIsReadOnly(true);

詳細は、次を参照してください。

12.12.6 JDBCフェッチ・サイズを使用した最適化方法

JDBCフェッチ・サイズは、より多くの行が必要な場合に、データベースからフェッチされる行数に関するヒントをJDBCドライバに提供します。

オブジェクトを返す問合せの数が多い場合、問合せで使用する行フェッチ・サイズを構成し、選択条件を満たしたときのデータベースのヒット数を減らすことにより、パフォーマンスを向上できます。

JDBCドライバのフェッチ・サイズのデフォルトは、ほとんどの場合10に設定されています。そのため、1000のオブジェクトを読み取っている場合は、フェッチ・サイズを256に増やすことで、問合せ結果のフェッチに必要な時間を大幅に短縮できます。最適なフェッチ・サイズは、必ずしも明確とはかぎりません。通常、最適なフェッチ・サイズは、予想される結果サイズの半分または4分の1です。結果セットのサイズかどうかわからず、フェッチ・サイズの設定を大きくしすぎたり小さくしすぎたりすると、パフォーマンスが低下することに注意してください。

例12-5に示すようにReadQueryのメソッドsetFetchSizeを使用して、問合せフェッチ・サイズを設定します。または、ReadQueryのメソッドsetMaxRowsを使用して、ResultSetで使用できる最大行数を制限できます。

例12-5 JDBCドライバ・フェッチ・サイズ

// Create query and set Employee as its reference class
ReadAllQuery query = new ReadAllQuery(Employee.class);
ExpressionBuilder builder = query.getExpressionBuilder();
query.setSelectionCriteria(builder.get("id").greaterThan(100));

// Set the JDBC fetch size
query.setFetchSize(50);

// Configure the query to return results as a ScrollableCursor
query.useScrollableCursor();

// Execute the query
ScrollableCursor cursor = (ScrollableCursor) session.executeQuery(query);

// Iterate over the results
while (cursor.hasNext()) {
    System.out.println(cursor.next().toString());
}
cursor.close();

この例では、問合せを実行する際に、JDBCドライバでデータベースから最初の50行を取得します(または、選択条件を満たす行が50に満たない場合はすべて)。cursor.next()をコールするたびに最初の50行を繰り返すため、JDBCドライバでは、ローカル・メモリーから行を返します。データベースから行を取得する必要はありません。51行目にアクセスする際に(選択条件を満たす行が50を超えると仮定した場合)、JDBCドライバでは、再度データベースにアクセスして次の50行を取得します。このようにすると、データベースに2回アクセスするだけで100行が返されます。

ゼロの値を指定すると(デフォルトであり、フェッチ・サイズは設定されない)、ヒントは無視され、JDBCドライバのデフォルトが使用されます。

自分のTopLinkアプリケーション内からJDBCドライバ・プロパティを構成する詳細は、97.5項「プロパティの構成」を参照してください。

12.12.7 カーソル付きストリームとスクロール可能カーソルを使用した最適化方法

問合せを構成し、カーソル付きJavaストリームまたはスクロール可能カーソルを使用して、データベースからデータを取得できます。これにより、完全なコレクションとしてではなく、扱いやすい増分形式で結果セットを表示できます。これは、結果セットのサイズが大きい場合に便利です。使用するJDBCドライバ・フェッチ・サイズの構成によって、さらにパフォーマンスを調整できます(12.12.6項「JDBCフェッチ・サイズを使用した最適化方法」を参照)。

スクロール可能カーソルの詳細は、111.11項「カーソルとストリームの問合せ結果の処理」を参照してください。

12.12.8 結果セットのページ区切りを使用した最適化方法

図12-2に示すように、ReadQueryメソッドsetMaxRows(maxRows)およびsetFirstResult(firstResult)を使用して、結果セットを複数のページで取得する問合せを構成できます(つまり、結果の一部をpageSize以下の結果のListとして取得できます)。

図12-2 結果セットのページ区切りの使用

図12-2の説明が続きます
「図12-2 結果セットのページ区切りの使用」の説明

この例では、最初の問合せの起動はpageSize=3maxRows=pageSizeおよびfirstResult=0で行われます。これにより、結果00から02のリストが返されます。

後続の問合せの起動ごとに、maxRows=maxRows+pageSizeおよびfirstResult=firstResult+pageSizeを増分します。これにより、各ページごとに結果03から0506から08などの新しいListが返されます。

この方法は通常、結果セット全体の処理が必要ではない場合に使用します。たとえば、結果セットを1ページごとにスキャンして特定の結果を検索し、目的のレコードが見つかったら問合せをやめる場合などです。

この方法がカーソルを使用する方法より優れている点は、サーバーとの状態(ライブ)接続が不要で、クライアントでのfirstResult索引の格納のみが必要であることです。このため、Webの結果のページ区切りに便利です。

詳細は、次を参照してください。

12.12.9 読取り最適化の例

TopLinkには、表12-13に示す読取り最適化機能が用意されています。

この項では、次の読取り最適化の例を示します。

表12-13 読取り最適化機能

機能 働き パフォーマンス向上のテクニック

作業ユニット

作業ユニットでのオブジェクトの変更を追跡します。

必要な追跡の量を最小限に抑えるには、変更されるオブジェクトのみを登録します。

詳細は、第113章「TopLinkトランザクションの概要」を参照してください。

インダイレクション(遅延ロード)

インダイレクション・オブジェクトを使用して、リレーションシップのロードと処理を遅らせます。

パフォーマンスが大幅に向上します。これにより、データベース・アクセスを最適化でき、TopLinkのキャッシュと作業ユニットで内部的に複数の最適化を実行できます。

ソフト・キャッシュ、弱アイデンティティ・マップ

データベースから読み取られるオブジェクトのクライアント側キャッシュを提供し、メモリーが少なくなったらオブジェクトをキャッシュから削除します。

データベース・コールを減らし、メモリーのパフォーマンスを向上させます。

詳細は、102.2.1項「キャッシュ・タイプおよびオブジェクト・アイデンティティ」を参照してください。

弱アイデンティティ・マップ

オブジェクトのクライアント側キャッシュを提供します。

データベース・アクセスを減らし、すべての参照オブジェクトのキャッシュを保持します。

詳細は、102.2.1項「キャッシュ・タイプおよびオブジェクト・アイデンティティ」を参照してください。

バッチ読取りと結合

多数の問合せを、より多くのデータを読み取る1つの問合せにまとめることで、データベース・アクセスを減らします。

読取り問合せの実行に要するデータベース・アクセスの数を大幅に減らします。

詳細は、109.2.1.9項「バッチ読取りの使用」および109.2.1.10項「ObjectLevelReadQueryを使用した結合読取りの使用」を参照してください。

部分オブジェクト読取りとフェッチ・グループ

オブジェクト属性の結果セットのサブセットを読み取ることが可能になります。

データベースから読み取られるデータの量が減少します。

詳細は、108.7.1.3項「部分オブジェクト問合せ」を参照してください。

フェッチ・グループの詳細は、16.2.4項「フェッチ・グループ」を参照してください。

レポート問合せ

部分オブジェクト読取りに似ていますが、オブジェクトではなくデータのみを返します。

集約やグループ化などの複雑なレポート機能をサポートします。また、オブジェクトをアプリケーションに読み込んで結果をローカルに計算するのではなく、複雑な結果をデータベース上で計算できます。

詳細は、108.7.5項「レポート問合せ」を参照してください。

読取り専用問合せ

読取り専用の未登録オブジェクトを返します。

読取り専用オブジェクトは、登録や変更の確認は必要ありません。

詳細は、12.12.5項「読取り専用問合せを使用した最適化方法」を参照してください。

JDBCフェッチ・サイズとReadQueryの最初の結果の最大行

選択条件を満たす行をすべて返すために必要な、データベースのヒット数が減少します。

詳細は、12.12.6項「JDBCフェッチ・サイズを使用した最適化方法」を参照してください。

カーソル

完全なコレクションとしてではなく、扱いやすい増分形式で大きな結果セットを表示できます。

詳細は、12.12.7項「カーソル付きストリームとスクロール可能カーソルを使用した最適化方法」を参照してください。

継承先サブクラスの外部結合

複数の問合せではなく、1つの問合せでそのサブクラスをすべて読み取ることができる継承スーパークラスに対する問合せを、ビューの有無に関係なく可能にします。

詳細は、12.12.9.5項「読取り例5: 継承先サブクラスの外部結合」を参照してください。

ソフト・アイデンティティ・マップ

弱アイデンティティ・マップの類似マップですが、相違点は、このマップでは弱参照ではなくソフト参照を使用することです。この方法では、完全なガベージ・コレクションを許可し、完全なキャッシュとアイデンティティの保証を行います。

サブキャッシュのオーバーヘッドなしにオブジェクトのキャッシュを最適化でき、メモリー残量が少ない場合はJVMでオブジェクトのガベージ・コレクションを行うことも可能です。

詳細は、102.2.1.3項「ソフト・アイデンティティ・マップ」を参照してください。


12.12.9.1 読取り例1: リストへの名前の表示

アプリケーションでは、ユーザーがリストから要素を選択するよう求められることがあります。リストにはオブジェクト内の情報のサブセットのみが表示されるため、データベースのオブジェクトの情報をすべて問い合せる必要はありません。

これらのタイプの操作を最適化するTopLink機能は、次のとおりです。

これらの機能を使用すると、リストに表示する必要のある情報のみを問い合せることができます。その後、ユーザーはリストからオブジェクトを選択できます。

12.12.9.1.1 部分オブジェクト読取り

部分オブジェクト読取りは、データベースで選択したレコードから、レコードに含まれるすべての情報ではなく、必要な情報のみを抽出することを目的とした問合せです。部分オブジェクト読取りではオブジェクトにデータが完全に移入されないため、部分的に読み取られたオブジェクトをキャッシュしたり、編集したりすることはできません。

部分オブジェクト問合せの詳細は、108.7.1.3項「部分オブジェクト問合せ」を参照してください。

例12-6では、従業員の姓のみがリストに表示されていますが、完全な従業員オブジェクトが問合せによって作成されています。最適化を行わない場合、問合せでは従業員のデータがすべて読み取られます。

例12-6 最適化なし

/* Read all the employees from the database, ask the user to choose one and return it. 
This must read in all the information for all the employees */
List list;

// Fetch data from database and add to list box
Vector employees = (Vector) session.readAllObjects(Employee.class);
list.addAll(employees);

// Display list box
....

// Get selected employee from list
Employee selectedEmployee = (Employee) list.getSelectedItem();

return selectedEmployee;

例12-7は、部分オブジェクト読取りの使用方法を示しています。部分オブジェクト読取りでは、従業員データの姓と主キーのみが読み取られます。その結果、データベースから読み取られるデータの量が減少します。

例12-7 部分オブジェクト読取りによる最適化

/* Read all the employees from the database, ask the user to choose one and return it. 
This uses partial object reading to read just the last names of the employees. 
Since TopLink automatically includes the primary key of the object, the full object can easily be 
read for editing */
List list;

// Fetch data from database and add to list box
ReadAllQuery query = new ReadAllQuery(Employee.class);
query.addPartialAttribute("lastName");

// The next line avoids a query exception
query.dontMaintainCache();
Vector employees = (Vector) session.executeQuery(query);
list.addAll(employees);

// Display list box
....

// Get selected employee from list
Employee selectedEmployee = (Employee)session.readObject(list.getSelectedItem());
return selectedEmployee;
12.12.9.1.2 レポート問合せ

レポート問合せを使用すると、オブジェクトとその関連オブジェクトのセットからデータを取得できます。レポート問合せは、データベースのレポート関数および機能をサポートします。

詳細は、108.5.2項「レポート問合せの結果」を参照してください。

例12-8は、レポート問合せを使用して従業員の姓のみを読み取る方法を示しています。この場合、例12-6のコードと比較して、データベースから読み取られるデータの量が減少し、従業員インスタンスがインスタンス化されるのを防止できます。

例12-8 レポート問合せによる最適化

/* Read all the employees from the database, ask the user to choose one and return it. 
The report query is used to read just the last name of the employees. 
Then the primary key stored in the report query result to read the real object */
List list;

// Fetch data from database and add to list box
ExpressionBuilder builder = new ExpressionBuilder();
ReportQuery query = new ReportQuery (Employee.class, builder);
query.addAttribute("lastName");
query.retrievePrimaryKeys();
Vector reportRows = (Vector) session.executeQuery(query);
list.addAll(reportRows);

// Display list box
....

// Get selected employee from list
ReportQueryResult result = (ReportQueryResult) list.getSelectedItem();
Employee selectedEmployee =     (Employee)result.readobject(Employee.Class,session);

最適化されていない例(例12-6)と例12-8のレポート問合せによる最適化の違いは小さいように見えますが、レポート問合せによってパフォーマンスが大幅に向上します。

12.12.9.1.3 フェッチ・グループ

フェッチ・グループは、部分オブジェクト読取りと類似していますが、オブジェクト読取りのキャッシュが可能です。複雑なグラフに対する多くの属性または参照属性(あるいはその両方)を持つオブジェクトの場合、どの属性が返され、いつオブジェクトが読み取られるのかを決定するフェッチ・グループを定義できます。TopLinkでは、フェッチ・グループにない属性についてgetメソッドをコールする場合、追加の問合せを自動的に実行するため、フェッチされないデータは必要ないことを確認してください。データの再フェッチはパフォーマンスの問題を生じる場合があります。

フェッチ・グループによる問合せの詳細は、111.3項「問合せでのフェッチ・グループの使用」を参照してください。

例12-9は、静的フェッチ・グループの使用方法を示しています。

例12-9 FetchGroupManagerを使用したフェッチ・グループによる問合せの構成

// Create static fetch group at the descriptor level
FetchGroup group = new FetchGroup("nameOnly");
group.addAttribute("firstName");
group.addAttribute("lastName");
descriptor.getFetchGroupManager().addFetchGroup(group);

// Use static fetch group at query level
ReadAllQuery query = new ReadAllQuery(Employee.class);
query.setFetchGroupName("nameOnly");

/* Only Employee attributes firstName and lastName are fetched.
   If you call the Employee get method for any other attribute, TopLink executes
   another query to retrieve all unfetched attribute values. Thereafter, 
   calling that get method will return the value directly from the object */

12.12.9.2 読取り例2: オブジェクトのバッチ読取り

アプリケーションがデータベースからデータを読み取る方法は、パフォーマンスに影響を与えます。たとえば、データベースからの行のコレクションの読取りは、各行を個別に読み取る場合よりも非常に高速です。

パフォーマンスに関してよく問題になるのは、1対1で相互に参照しているオブジェクトのコレクションを読み取る場合です。この場合、通常はソース行を読み取るための1回の読取り操作と、1対1リレーションシップの各ターゲット行に対して1回ずつのコールが必要です。

必要な読取り操作の回数を減らすには、結合とバッチ読取りを使用します。例12-10は、1対1で相互に参照しているオブジェクトのコレクションを取得するために必要な、最適化されていないコードを示しています。例12-11例12-12は、結合とバッチ読取りを使用して効率を高める方法を示しています。

例12-10 最適化なし

/* Read all the employees, and collect their address' cities. This takes N + 1 
   queries if not optimized */

// Read all the employees from the database. This requires 1 SQL call
Vector employees = session.readAllObjects(Employee.class,
   new ExpressionBuilder().get("lastName").equal("Smith"));

//SQL: Select * from Employee where l_name = 'Smith'

// Iterate over employees and get their addresses.
// This requires N SQL calls
Enumeration enum = employees.elements();
Vector cities = new Vector();
while(enum.hasMoreElements()) {
   Employee employee = (Employee) enum.nextElement();
   cities.addElement(employee.getAddress().getCity());
}
//SQL: Select * from Address where address_id = 123, etc 

例12-11 結合による最適化

/* Read all the employees; collect their address' cities. Although the code
   is almost identical because joining optimization is used it takes only 1 
   query */

// Read all the employees from the database using joining. 
// This requires 1 SQL call
ReadAllQuery query = new ReadAllQuery(Employee.class);
ExpressionBuilder builder = query.getExpressionBuilder();
query.setSelectionCriteria(builder.get("lastName").equal("Smith"));
query.addJoinedAttribute("address");
Vector employees = session.executeQuery(query);

/* SQL: Select E.*, A.* from Employee E, Address A where E.l_name = 'Smith' and 
   E.address_id = A.address_id Iterate over employees and get their addresses. 
   The previous SQL already read all the addresses, so no SQL is required */
Enumeration enum = employees.elements();
Vector cities = new Vector();
while (enum.hasMoreElements()) {
    Employee employee = (Employee) enum.nextElement();
    cities.addElement(employee.getAddress().getCity());
}

例12-12 バッチ読取りによる最適化

/* Read all the employees; collect their address' cities. Although the code 
   is almost identical because batch reading optimization is used it takes only 
   2 queries */

// Read all the employees from the database, using batch reading. 
// This requires 1 SQL call, note that only the employees are read 
ReadAllQuery query = new ReadAllQuery(Employee.class);
ExpressionBuilder builder = query.getExpressionBuilder();
query.setSelectionCriteria(bulder.get("lastName").equal("Smith"));
query.addBatchReadAttribute("address");
Vector employees = (Vector)session.executeQuery(query);

// SQL: Select * from Employee where l_name = 'Smith'

// Iterate over employees and get their addresses.
// The first address accessed will cause all the addresses
// to be read in a single SQL call
Enumeration enum = employees.elements();
Vector cities = new Vector();
while (enum.hasMoreElements()) {
    Employee employee = (Employee) enum.nextElement();
    cities.addElement(employee.getAddress().getCity());
    // SQL: Select distinct A.* from Employee E, Address A 
    // where E.l_name = 'Smith' and E.address_id = A.address_i
}

2フェーズ方式の問合せ(例12-11例12-12)は、データベースに2回しかアクセスしないため、例12-10に示した方法よりも非常に高速です。

結合を使用すると、ほとんどの状況でパフォーマンスが大幅に向上します。バッチ読取りを使用すると、ValueHolderを通じてロードを遅らせることができるため、パフォーマンスがさらに向上します。また、ターゲット・オブジェクトが共有されている場合は、はるかに優れたパフォーマンスが得られます。

たとえば、例12-10例12-11および例12-12の従業員が同じ住所に住んでいる場合、バッチ読取りではSQLのDISTINCTコールを使用して重複データがフィルタ処理されるため、結合を使用したときよりもずっと少ないデータが読み取られます。

バッチ読取りと結合は1対1、1対多、多対多、ダイレクト・コレクション、ダイレクト・マップおよび集約コレクション・マッピングで使用可能です。1対多結合では重複データが大量に返されるため、通常はバッチ読取りよりも効率的ではないことに注意してください。

12.12.9.3 読取り例3: 複雑なカスタムSQL問合せの使用

TopLinkは、高度な問合せメカニズムを提供します。しかし、アプリケーションで複雑な問合せが必要とされる場合は、ダイレクトSQLコールまたはストアド・プロシージャ・コールが最善の解決策となることもあります。

SQLコールの実行の詳細は、108.9.1.1項「SQLCall」を参照してください。

12.12.9.4 読取り例4: ビュー・オブジェクトの使用

アプリケーション操作の中には、1つのみではなく、複数のオブジェクトの情報を必要とするものがあります。これは実現が難しく、リソースに負荷がかかる可能性もあります。例12-13は、複数のオブジェクトの情報を読み取る、最適化されていないコードを示しています。

例12-13 最適化なし

/* Gather the information to report on an employee and return the summary of the 
   information. In this situation, a hash table is used to hold the report 
   information. Notice that this reads a lot of objects from the database, but 
   uses very little of the information contained in the objects. This may take 5
   queries and read in a large number of objects */

public Hashtable reportOnEmployee(String employeeName) {
    Vector projects, associations;
    Hashtable report = new Hashtable();
    // Retrieve employee from database
    Employee employee = session.readObject(Employee.class,
       new ExpressionBuilder.get("lastName").equal(employeeName));
    // Get all the projects affiliated with the employee
    projects = session.readAllObjects(Project.class,
      "SELECT P.* FROM PROJECT P," +
      "EMPLOYEE E WHERE P.MEMBER_ID = E.EMP_ID AND E.L_NAME = " +
      employeeName);
    // Get all the associations affiliated with the employee
    associations = session.readAllObjects(Association.class, "SELECT A.* " +
      "FROM ASSOC A, EMPLOYEE E WHERE A.MEMBER_ID = E.EMP_ID AND E.L_NAME = "
      + employeeName);
    report.put("firstName", employee.getFirstName());
    report.put("lastName", employee.getLastName());
    report.put("manager", employee.getManager());
    report.put("city", employee.getAddress().getCity());
    report.put("projects", projects);
    report.put("associations", associations);
    return report;
}

このような場合にアプリケーションのパフォーマンスを向上させるには、新規の読取り専用オブジェクトを定義してこの情報をカプセル化し、定義したオブジェクトをデータベース上のビューにマップします。オブジェクトを読取り専用に設定するには、オブジェクトのディスクリプタを読取り専用として構成します(119.3項「読取り専用ディスクリプタの構成」を参照)。

例12-14 ビュー・オブジェクトによる最適化

CREATE VIEW NAMED EMPLOYEE_VIEW AS (SELECT F_NAME = E.F_NAME, L_NAME = E.L_NAME,EMP_ID = E.EMP_ID, 
MANAGER_NAME = E.NAME, CITY = A.CITY, NAME = E.NAME
FROM EMPLOYEE E, EMPLOYEE M, ADDRESS A
WHERE E.MANAGER_ID = M.EMP_ID
AND E.ADDRESS_ID = A.ADDRESS_ID)

EmployeeReportクラスのディスクリプタを次のように定義します。

  • ディスクリプタを通常どおり定義します。ただし、tableNameEMPLOYEE_VIEWとして指定します。

  • レポートに必要な属性のみをマップします。numberOfProjectsassociationsの場合、トランスフォーメーション・マッピングを使用して、必要なデータを取得します。

これで、他のTopLink対応オブジェクトと同様に、データベースからレポートを問い合せることができます。

例12-15 例12-14のレポートの表示

/* Return the report for the employee */
public EmployeeReport reportOnEmployee(String employeeName) {
    EmployeeReport report;
    report = (EmployeeReport) session.readObject(EmployeeReport.class,
      new ExpressionBuilder.get("lastName").equal(employeeName));
    return report;
}

警告:

検証されていないSQL文字列をメソッド(たとえばreadAllObjects(Class class, String sql)およびreadObject(Class class, String sql)メソッド)に渡せるようにすると、SQLインジェクション攻撃に対してアプリケーションが脆弱になります。


12.12.9.5 読取り例5: 継承先サブクラスの外部結合

複数の表にわたる継承階層があり、ルート・クラスに頻繁に問い合せる場合は、外部結合の使用を検討してください。これにより、外部結合は、複数の問合せではなく、1つの問合せでそのサブクラスをすべて読み取ることができる継承スーパークラスに対する問合せに使用できます。

ただし、一部のデータベースでは、外部結合はデフォルトの複数の問合せのメカニズムより効率が悪い場合があります。

継承の詳細は、16.2.2項「ディスクリプタと継承」を参照してください。

継承の問合せの詳細は、111.6項「継承階層に対する問合せ」を参照してください。

12.12.10 書込み最適化の例

TopLinkには、表12-14に示す書込み最適化機能が用意されています。

この項では、次の書込み最適化の例を示します。

表12-14 書込み最適化機能

機能 パフォーマンスへの影響

作業ユニット

変更されたフィールドとオブジェクトのみを更新することで、パフォーマンスが向上します。

変更されるオブジェクトのみを登録することで、(負荷の高い)必要な追跡の量が最小限に抑えられます。

詳細は、第113章「TopLinkトランザクションの概要」を参照してください。

注意: 作業ユニットでは、クラスが読取り専用としてマークされます(119.3項「読取り専用ディスクリプタの構成」および115.2項「読取り専用クラスの宣言」を参照)。これにより、変更のないオブジェクトの追跡が回避されます。

バッチ書込み

トランザクションの挿入、更新および削除の全コマンドを1つのデータベース・コールにグループ化できます。そうすることで、データベースに対するコールの数が大幅に減少します(12.12.10.1.2項「バッチ書込みおよびパラメータ使用のSQL」を参照)。

パラメータ使用のSQL

頻繁に実行するSQL文のパフォーマンスを向上させます(12.12.1項「パラメータ使用のSQLとプリコンパイルされたSQL文のキャッシュを使用した最適化方法」を参照)。

順序番号の事前割当て

挿入のパフォーマンスが大幅に向上します(12.12.10.1.3項「順序番号の事前割当て」を参照)。

マルチプロセッシング

バッチ・ジョブを複数のスレッドにわたって分割することで、カーソル付きストリームからの読取りを同期化し、1つのマシンでも複数の作業ユニットを並行して使用し、パフォーマンスを向上させることができます(12.12.10.1.4項「マルチプロセッシング」を参照)。

存在確認コールの代替手段

キャッシュで存在確認のチェックをするか、オブジェクトが存在すると仮定することにより、特定の状況での書込みオブジェクトに対する存在確認コールを防止できます(117.7項「プロジェクト・レベルでの存在チェックの構成」または119.17項「ディスクリプタ・レベルでのキャッシュ存在チェックの構成」および115.1.3項「登録と存在チェックの使用方法」を参照)。

変更追跡

書込みおよびトランザクション読取りのパフォーマンスを向上させます(113.2.3項「作業ユニットおよび変更ポリシー」および119.30項「変更ポリシーの構成」を参照)。

独立クライアント・セッション

書込み専用または非キャッシュ(独立)オブジェクトの場合、キャッシュしていなかった場合にこれからキャッシュする際のオーバーヘッドを回避するため、作業ユニットの分離レベルは「常に分離」に設定する必要があります(102.2.7項「キャッシュの分離」を参照)。


12.12.10.1 書込み例: バッチ書込み

書込みのパフォーマンスに関する最も一般的な問題は、バッチ・ジョブによって大量のデータがデータベースに挿入されるときに発生します。たとえば、あるデータベースから大量のデータをロードして別のデータベースに移行するバッチ・ジョブについて考えてみます。この操作に関与するオブジェクトには次のような特徴があるとします。

  • リレーションシップを持たない、単純な個別のオブジェクトである。

  • 生成された順序番号を主キーとして使用するオブジェクトである。

  • 同じく順序番号を使用する住所を持つオブジェクトである。

バッチ・ジョブは、10,000名の従業員のレコードを最初のデータベースからロードして、ターゲット・データベースに挿入します。最適化を行わない場合、バッチ・ジョブは、ソース・データベースからすべてのレコードを読み取り、ターゲット・データベースから作業ユニットを取得し、すべてのオブジェクトを登録して、作業ユニットをコミットします。

例12-16 最適化なし

/* Read all the employees, acquire a unit of work, and register them */

// Read all the employees from the database. This requires 1 SQL call,
// but will be very memory intensive as 10,000 objects will be read
Vector employees = sourceSession.readAllObjects(Employee.class);

//SQL: Select * from Employee

// Acquire a unit of work and register the employees
UnitOfWork uow = targetSession.acquireUnitOfWork();
uow.registerAllObjects(employees);
uow.commit();

// SQL: Begin transaction
// SQL: Update Sequence set count = count + 1 where name = 'EMP'
// SQL: Select count from Sequence
// SQL: ... repeat this 10,000 times + 10,000 times for the addresses ...
// SQL: Commit transaction
// SQL: Begin transaction
// SQL: Insert into Address (...) values (...)
// SQL: ... repeat this 10,000 times
// SQL: Insert into Employee (...) values (...)
// SQL: ... repeat this 10,000 times
// SQL: Commit transaction

このバッチ・ジョブでは60,000回のSQL実行が必要なため、パフォーマンスが低くなります。また、大量のデータをメモリーに読み込むため、メモリーのパフォーマンスの問題が発生する可能性もあります。TopLinkには、このバッチ・ジョブのパフォーマンスを向上させる最適化機能がいくつか用意されています。

この操作を改善するには、次のようにします。

12.12.10.1.1 カーソル

例12-16の問合せを最適化するには、カーソル付きストリームを使用して、ソース・データベースから従業員を読み取ります。ソース・データベースとターゲット・データベースの両方で、ハードまたはソフト・キャッシュ・アイデンティティ・マップのかわりに弱アイデンティティ・マップを使用することもできます。

メモリーの問題が発生する可能性をなくすには、各読取りの後にreleasePreviousメソッドを使用して、カーソルを100単位でストリーム化します。100名の従業員のバッチをそれぞれ新規作業ユニットに登録し、コミットします。

これで実行されるSQLの量は減りませんが、メモリー不足の問題が発生する可能性はなくなります。システムのメモリーが不足すると、時間の経過とともにパフォーマンスが低下していき、ディスク上のメモリー・スワッピングが原因で過度のディスク・アクティビティが生じます。

詳細は、12.12.7項「カーソル付きストリームとスクロール可能カーソルを使用した最適化方法」を参照してください。

12.12.10.1.2 バッチ書込みおよびパラメータ使用のSQL

バッチ書込みを使用すると、SQL文のグループを1つの文にまとめて、1つのデータベース実行としてデータベースに送信することができます。この機能により、アプリケーションとサーバー間の通信時間が短縮され、パフォーマンスが大幅に向上します。

LoginのメソッドuseBatchWritingを使用して、バッチ書込みのみ(動的バッチ書込み)を有効にできます。例12-16にバッチ書込みを追加する場合、100名の従業員のバッチをそれぞれ1回のSQL実行として実行します。これにより、SQL実行の回数は、20,200回から300回に減少します。

バッチ書込み、パラメータ使用のSQL(パラメータ使用のバッチ書込み)およびプリコンパイルされたSQL文のキャッシュも有効にできます。パラメータ使用のSQLを使用すると、SQL実行の準備コンポーネントを回避できます。これにより、SQL実行を準備する負荷が回避できるため、書込みのパフォーマンスが向上します。従業員と住所ごとにそれぞれ1文ずつ取得するパラメータ使用のバッチ書込みの場合、SQL実行の回数は20,200回から400回に減少します。これは動的バッチ書込みのみの場合よりも多いですが、パラメータ使用のバッチ書込みでは、すべての解析を回避できるため、全体的に見て非常に効率的です。

パラメータ使用のSQLにより、SQL実行の準備コンポーネントが不要になりますが、実行の回数は減少しません。このため、パラメータ使用のSQLのみでは、バッチ書込みほど効果的ではありません。ただし、データベースがバッチ書込みをサポートしていない場合は、パラメータ使用のSQLによってパフォーマンスが向上します。例12-16にパラメータ使用のSQLを追加しても、20,200回のSQL実行が必要であることは変わりませんが、SQLのPREPAREの数が4つに減少します。

詳細は、12.11.3項「バッチ書込みを使用した最適化方法」を参照してください。

12.12.10.1.3 順序番号の事前割当て

SQLの選択コールはSQLの変更コールよりもリソースの消費量が多いため、発行する選択コールの数を減らすことで、パフォーマンスの大幅な向上を実現できます。例12-16のコードでは、選択コールを使用して順序番号を取得しています。順序番号の事前割当てを使用すると、パフォーマンスを大幅に向上させることができます。

TopLinkでは、ログイン・オブジェクトで順序の事前割当てサイズを構成できます(デフォルト・サイズは50です)。例12-16では、事前割当てサイズ1を使用して、この点を示しています。12.12.10.1.1項「カーソル」で述べたように、データを100単位のバッチにストリーム化する場合、順序の事前割当てサイズを100に設定します。例の従業員と住所がともに順序番号を使用しているため、同じ順序を共有させれば、パフォーマンスはさらに向上します。事前割当てサイズを200に設定した場合、SQL実行の回数は60,000回から20,200回に減少します。

順序付けの事前割当ての詳細は、18.2.3項「順序付けと事前割当てサイズ」を参照してください。

12.12.10.1.4 マルチプロセッシング

複数のプロセス(複数のマシン)を使用して、バッチ・ジョブをいくつかの小さなジョブに分割することができます。前述の例では、バッチ・ジョブを複数のスレッドにわたって分割することで、カーソル付きストリームからの読取りを同期化し、1つのマシンで複数の作業ユニットを並行して使用できます。

その場合、マシンにプロセッサが1つしかなくても、プロセッサではSQL実行固有の待機時間が利用されるため、パフォーマンスが向上します。1つのスレッドがサーバーからのレスポンスを待っている間に、別のスレッドは待機サイクルを利用して、自身のデータベース操作を処理します。

例12-17は、この例として最適化されたコードを示しています。マルチプロセッシングは使用されていませんので、注意してください。

例12-17 完全な最適化

/* Read each batch of employees, acquire a unit of work, and register them */
targetSession.getLogin().useBatchWriting();
targetSession.getLogin().setSequencePreallocationSize(200);
targetSession.getLogin().bindAllParameters();
targetSession.getLogin().cacheAllStatements();
targetSession.getLogin().setMaxBatchWritingSize(200);

// Read all the employees from the database into a stream. 
// This requires 1 SQL call, but none of the rows will be fetched.
ReadAllQuery query = new ReadAllQuery(Employee.class);
query.useCursoredStream();
CursoredStream stream;
stream = (CursoredStream) sourceSession.executeQuery(query);
//SQL: Select * from Employee. Process each batch
while (! stream.atEnd()) {
    Vector employees = stream.read(100);
    // Acquire a unit of work to register the employees
    UnitOfWork uow = targetSession.acquireUnitOfWork();
    uow.registerAllObjects(employees);
    uow.commit();
}
//SQL: Begin transaction
//SQL: Update Sequence set count = count + 200 where name = 'SEQ'
//SQL: Select count from Sequence where name = 'SEQ'
//SQL: Commit transaction
//SQL: Begin transaction
//BEGIN BATCH SQL: Insert into Address (...) values (...)
//... repeat this 100 times
//Insert into Employee (...) values (...)
//... repeat this 100 times
//END BATCH SQL:
//SQL: Commit transactionJava optimization

12.13 作業ユニットの最適化

作業ユニットを使用する際に最高のパフォーマンスを得るため、次のヒントを考慮してください。

パフォーマンスの測定で、作業ユニットのコミット中にパフォーマンスの問題があることを示している場合は、関連するオブジェクトのタイプとその一般的な変更方法に応じて、オブジェクト・レベルまたは属性レベル変更追跡の使用を検討してください。詳細は、113.2.3項「作業ユニットおよび変更ポリシー」を参照してください。

12.14 ウィービングによる最適化

ウィービングを有効にしてパフォーマンスを向上させることをお薦めします。ウィービングと変更追跡を使用すると、特にトランザクションのパフォーマンスを大幅に向上できます。

ウィービングは、遅延ロード(インダイレクション)と変更追跡を透過的に構成することに使用されるのみでなく、多数の内部最適化にも使用されます。

詳細は、2.10項「ウィービングの使用」を参照してください。

12.15 アプリケーション・サーバーとデータベースの最適化

アプリケーション・サーバーおよびデータベースを正しく構成すると、パフォーマンスおよびスケーラビリティに大きく影響します。TopLinkアプリケーションおよび永続性の他に、アプリケーションのこれらの主要コンポーネントを正しく最適化していることを確認してください。

アプリケーション・サーバーまたはJava EEサーバーの場合は、メモリー、スレッド・プール・サイズおよび接続プール・サイズが、サーバーで予測されるロードに十分であり、JVMの構成が最適であることを確認します。

最適なパフォーマンスと予測されるロードに対応できるように、データベースが正しく構成されていることを確認します。

12.16 XMLのバイナリ・データの保存および取得の最適化

Java API for XML Web Services(JAX-WS)を使用する場合には、XMLバイナリ添付ファイルを使用して、XMLのバイナリ・データの保存および取得を最適化できます。データは、base64 BLOBとして保存するのではなく、相手方で取得できるようにMultipurpose Internet Mail Extensions(MIME)添付ファイルとして送信することによって、最適化できます。

XMLバイナリ添付ファイルを利用するには、oracle.toplink.ox.attachment.XMLAttachmentMarshallerまたはXMLAttachmentUnmarshallerのインスタンスをバインディング・フレームワークに登録します。バイナリ・データはマーシャリング処理中にXMLAttachmentMarshallerに渡されます。XMLAttachmentMarshallerは、後でデータ取得に使用できるIDを取得する際に必要となります。

TopLinkランタイムでは、MtOMスタイルおよびSwaRefスタイルの添付ファイルがサポートされます。

TopLinkでは、添付ファイルとして次のJavaタイプがサポートされます。

これらのタイプのJAXBクラスをベースにして、スキーマおよびマッピングを生成できます。

どのマッピングを添付ファイルとして扱うかを構成し、それらの添付ファイルのMIMEタイプを設定できます。構成を行う際には、次のJAXB注釈を使用します。

JAXB注釈の詳細は、JAXBの仕様(http://jcp.org/aboutJava/communityprocess/pfd/jsr222/index.html)の第8章を参照してください。

また、マッピングを添付ファイルにする場合は、マッピング内のスキーマ・タイプをバイナリ(base64BinaryまたはswaRefのいずれか)に設定する必要があります。


注意:

TopLinkでは、添付ファイルとしてのオブジェクトの扱いを、マッピング単位でオーバーライドできます。

次の例について考えてください。

例12-18 SwaRefの使用

public class Employee {

    @XmlAttachmentRef
    public DataHandler photo;
    ...
}

このコードでは、次のタイプのXMLスキーマが生成されます。

<xs:complexType name="employee">
    <xs:sequence>
        <xs:element name="photo" type="xs:swaRef"/>
    </xs:sequence>
</xs:complexType>

XMLは次のようになります。

<employee>
    <photo>attachment_id</photo>
</employee>

例12-19 MimeTypeなしでのMtOMの使用

public class Employee {

    public java.awt.Image photo;
    ...
}

このコードでは、次のタイプのXMLスキーマが生成されます。

<xs:complexType name="employee">
    <xs:sequence>
        <xs:element name="photo" type="base64Binary"/>
    </xs:sequence>
</xs:complexType>

XMLは次のようになります。

<employee>
    <photo>
        <xop:Include href="attachment_id"/>
    </photo>
</employee>

例12-20 MimeTypeありでのMtOMの使用

public class Employee {

    @XmlMimeType("image/jpeg")
    public java.awt.Image photo;
    ...
}

このコードでは、次のタイプのXMLスキーマが生成されます。

<xs:complexType name="employee">
    <xs:sequence>
        <xs:element name="photo"
            ns:expectedContentTypes="image/jpeg"
            type="xs:base64Binary"/>
    </xs:sequence>
</xs:complexType>

XMLは次のようになります。

<employee>
    <photo>
        <xop:Include href="attachment_id"/>
    </photo>
</employee>

例12-21 インラインに強制指定したバイナリ・オブジェクトの使用

public class Employee {

    @XmlInlineBinaryData
    public java.awt.Image photo;
    ...
}

このコードでは、次のタイプのXMLスキーマが生成されます。

<xs:complexType name="employee">
    <xs:sequence>
        <xs:element name="photo" type="xs:base64Binary"/>
    </xs:sequence>
</xs:complexType>

XMLは次のようになります。

<employee>
    <photo>ASWIUHFD1323423OIJEUFHEIUFWE134DFO3IR3298RY== </photo>
</employee>

JAXBを使用していない場合、バイナリ・データを扱うにはoracle.toplink.ox.mappings.XMLBinaryDataMappingおよびXMLBinaryDataCollectionMapping APIを使用します。詳細は、53.12項「XMLバイナリ・データ・マッピング」および53.13項「XMLバイナリ・データ・コレクション・マッピング」を参照してください。

12.16.1 添付ファイルのマーシャラおよびアンマーシャラの使用方法

様々なタイプのXML添付ファイルを追加および取得するには、TopLinkのインタフェースXMLAttachmentMarshallerおよびXMLAttachmentUnmarshallerを実装します。XMLAttachmentMarshallerのインスタンスがXMLMarshallerに、XMLAttachmentUnmarshallerのインスタンスがXMLUnmarshallerにあります。

添付ファイルのマーシャラおよびアンマーシャラを設定および取得する場合には、それぞれ次のXMLMarshallerおよびXMLUnmarshallerメソッドを使用します。

  • setAttachmentMarshaller(XMLAttachmentMarshaller am)

  • getAttachmentMarshaller()

  • setAttachmentUnmarshaller(XMLAttachmentUnmarshaller au)

  • getAttachmentUnmarshaller()

例12-22は、添付ファイルのマーシャラをアプリケーション内で使用する方法を示します。

例12-22 添付ファイルのマーシャラの使用

...
XMLMarshaller marshaller = context.createMarshaller();
XMLAttachmentMarshaller am = new EmployeeAttachmentMarshaller();
marshaller.setAttachmentMarshaller(am);
...

この例が有効であるためには、XMLスキーマ・タイプをswaRefに設定しておく必要があります。

詳細は、47.1.1.3.1項「TopLink XMLContextを使用する」を参照してください。