ナビゲーションをスキップ

WebLogic JDBC プログラマーズ ガイド

  前 次 前/次ボタンと目次ボタンとの区切り線 目次  

JDBC アプリケーションのパフォーマンス チューニング

以下の節では、JDBC アプリケーションから最高のパフォーマンスを引き出す方法について説明します。

 


WebLogic のパフォーマンス向上機能

WebLogic には、JDBC アプリケーションのパフォーマンスを向上させるための機能がいくつか用意されています。

接続プールによるパフォーマンスの向上

DBMS への JDBC 接続の確立には非常に時間がかかる場合があります。JDBC アプリケーションでデータベース接続の開閉を繰り返す必要がある場合、これは重大なパフォーマンスの問題となります。WebLogic 接続プールは、こうした問題を効率的に解決します。

WebLogic Server を起動すると、接続プール内の接続が開き、すべてのクライアントが使用できるようになります。クライアントが接続プールの接続を閉じると、その接続はプールに戻され、他のクライアントが使用できる状態になります。つまり、接続そのものは閉じられません。プール接続の開閉には、ほとんど負荷がかかりません。

どのくらいの数の接続をプールに作成すればよいでしょうか。トランザクションごとに関連する接続プールから 1 つの接続を使用する典型的なアプリケーションの場合、各接続プールには、同時に実行する可能性のあるトランザクションごとに 1 つの接続が必要です。トランザクションの数はサーバが実行する実行スレッド数です。スレッドが完了すると、接続は速やかに解放され、スレッドが次のトランザクションを開始するときに再利用されます。したがって、プール サイズは通常、実行スレッドの数と一致します。接続プールがデータベース接続を定期的にテストするようにコンフィグレーションされている場合、サイズをさらに増やします。

Statement とデータのキャッシング

DBMS のアクセスでは大量にリソースを消費します。プログラムで prepared statement または callable statement を再利用する場合、または複数のアプリケーションでの共有や、各接続どうしでの存続が可能な頻繁に使用するデータにアクセスする場合は、次を使用してデータをキャッシュできます。

 


ベスト パフォーマンスのためのアプリケーション設計

データベース アプリケーションのパフォーマンスの良し悪しはほとんどの場合、アプリケーション言語ではなく、アプリケーションがどのように設計されているかによって決定されます。クライアントの数と場所、DBMS テーブルおよびインデックスのサイズと構造、およびクエリの数とタイプは、すべてアプリケーションのパフォーマンスに影響を与えます。

以下では、すべての DBMS に当てはまる一般的なヒントを示します。また、アプリケーションで使用する特定の DBMS のドキュメントによく目を通しておくことも重要です。

1. データをできるだけデータベースの内部で処理する

DBMS アプリケーションのパフォーマンスに関する最も深刻な問題は、生データを不必要に移動することから発生します。これは、生データをネットワーク上で移動する場合にも、単に DBMS のキャッシュに出し入れする場合にも言えることです。こうした無駄を最小限に抑えるための良い方法は、クライアントが DBMS と同じマシンで動作している場合でも、ロジックをクライアントではなくデータの格納場所、つまり DBMS に置くことです。実際のところ、一部の DBMS では、1 個の CPU を共有するファット クライアントとファット DBMS はパフォーマンスの致命的な低下をもたらします。

大部分の DBMS は、ストアド プロシージャという、データの格納場所にロジックを置くための理想的なツールを備えています。ストアド プロシージャを呼び出して 10 個の行を更新するクライアントと、同じ行を取得および変更し、UPDATE 文を送信してその変更を DBMS に保存するクライアントの間には、パフォーマンスに大きな違いがあります。

また、DBMS のドキュメントを参照して、DBMS 内のキャッシュ メモリの管理について調べる必要もあります。一部の DBMS (Sybase など) は、DBMS に割り当てられた仮想メモリを分割し、特定のオブジェクトがキャッシュの固定領域を独占的に使用できるようにする機能を備えています。この機能を使用すると、重要なテーブルまたはインデックスをディスクから一度読み出しておくことで、ディスクに再度アクセスしなくてもすべてのクライアントがそれらを使用できるようになります。

2. 組み込み DBMS セットベース処理を使用する

SQL は、セット処理言語です。DBMS は、完全にセットベース処理を行うように設計されています。データベースの 1 行へのアクセスは、例外なくセットベースの処理より遅く、また DBMS によっては実装が不完全です。たとえば、従業員 100 名に関するデータが格納されている 4 つのテーブルがある場合、全従業員について各テーブルを一度に更新する方が、従業員 1 名ごとに各テーブルを 100 回更新するより常に高速です。

あまりに複雑すぎて 1 行ずつ処理する以外に方法がないと考えられていた処理の多くが、セットベースの処理に書き換えられ、パフォーマンスの向上を実現しています。たとえば、ある有名な給与管理アプリケーションは、巨大で低速な COBOL アプリケーションから、連続実行される 4 つのストアド プロシージャに変換されました。この結果、マルチ CPU マシンで何時間もかかった処理が、より少ないリソースで 15 分で実行できるようになりました。

3. クエリを効率化する

ユーザからよく尋ねられる質問に、「特定の結果セットで返される行数はどのくらいか」というものがあります。すべての行を取り出さずに調べる唯一の方法は、次のように count キーワードを使用して同じクエリを発行することです。

SELECT count(*) from myTable, yourTable where ...

これにより、関連するデータには変更がなかった場合に、オリジナルのクエリが戻すべき行数が返されます。また関連するデータに影響を与えるその他の DBMS アクティビティが起きた場合に、クエリを実行すれば、実際の行数は変わります。

ただし、これはリソースを大量に消費する処理であることに注意してください。元のクエリによっては、DBMS は行を送信するのと同じくらいの処理を行って行をカウントする必要があります。

アプリケーションのクエリは、実際にどのようなデータが必要なのかをできる限り具体的に指定する必要があります。たとえば、まず一時テーブルに抽出し、カウントだけを返し、次に限定された 2 番目のクエリを送信して一時テーブル内の行のサブセットだけを返すようにします。

クライアントが本当に必要なデータだけを抽出することが、きわめて重要です。ISAM (リレーショナル データベース以前のアーキテクチャ) から移植された一部のアプリケーションでは、実際に必要なのは最初の数行だけであっても、テーブル内のすべての行を選択するクエリが送信されます。また、最初に取得する行を得るために「sort by」句を使用するアプリケーションもあります。このようなデータベース クエリは、パフォーマンスを不必要に低下させます。

SQL を適切に使用すると、こうしたパフォーマンス上の問題を回避できます。たとえば、高額給与の社員のうち上位 3 人だけのデータが必要な場合、クエリを適切に行うには、相関サブクエリを使用します。表 3-1 に SQL 文が返す全体の表を示します。

select * from payroll 

表 3-1 返されたすべての結果

名前

給与

Joe

10

Mike

20

Sam

30

Tom

40

Jan

50

Ann

60

Sue

70

Hal

80

May

80


 

相関サブクエリ

select p.name, p.salary from payroll p 
where 3 >= (select count(*) from payroll pp 
where pp.salary >= p.salary);

表 3-2 に示すように、このクエリでは、より少ない結果が返されます。

表 3-2 サブクエリの結果

名前

給与

Sue

70

Hal

80

May

80


 

このクエリでは、上位 3 名の高所得者の名前と給与が登録された 3 行だけが返されます。このクエリでは、給与テーブル全体をスキャンし、次に各行について内部ループで給与テーブル全体を再スキャンして、ループの外でスキャンした現在の行より高額の給与が何件あるかを調べます。この処理は複雑なように見えるかもしれませんが、DBMS はこの種の処理では SQL を効率的に使用するように設計されています。

4. トランザクションを単一バッチにする

可能な限り、一連のデータ処理を収集し、更新トランザクションを次のような単一の文で発行してください。

BEGIN TRANSACTION

UPDATE TABLE1...

INSERT INTO TABLE2

DELETE TABLE3

COMMIT

この方法により、別個の文とコミットを使用するよりもパフォーマンスが向上します。バッチ内で条件ロジックと一時テーブルを使用する場合でも、DBMS はさまざまな行とテーブルに必要なすべてのロックを取得し、ワン ステップで使用および解放するので、この方法は望ましいと言えます。別個の文とコミットを使用すると、クライアントと DBMS 間の転送が増加し、DBMS 内のロック時間が長くなります。こうしたロックにより、他のクライアントはそのデータにアクセスできなくなり、複数の更新がさまざまな順序でテーブルを更新できるかによって、デッドロックが発生する可能性があります。

警告 : ユニーク キー制約の違反などによって上記のトランザクション中の任意の文が適切に実行されなかった場合は、条件 SQL ロジックを追加して文の失敗を検出し、トランザクションをコミットせずにロールバックする必要があります。上の例の場合、INSERT 文が失敗すると、ほとんどの DBMS は挿入の失敗を示すエラー メッセージを返しますが、2 番目と 3 番目の文の間でメッセージを取得したかのように動作して、コミットが行われてしまいます。Microsoft SQL Server には、SQL set xact_abort on の実行によって有効となる接続オプションがあります。このオプションを使用すると、文が失敗した場合にトランザクションが自動的にロールバックされます。

5. DBMS トランザクションがユーザ入力に依存しないようにする

アプリケーションが、'BEGIN TRAN' と、更新のために行またはテーブルをロックする SQL を送信する場合、ユーザのキー入力がなければトランザクションをコミットできないようにアプリケーションを設計してはなりません。ユーザがキー入力をせずに昼食に出かけてしまうと、ユーザが戻ってくるまで DBMS 全体がロックされてしまいます。

トランザクションの作成と完了にユーザ入力が必要な場合は、オプティミスティック ロックを使用します。簡単に言えば、オプティミスティック ロックではクエリと更新でタイムスタンプとトリガが使用されます。クエリは、トランザクション中にデータをロックすることなく、タイムスタンプ値を持つデータを選択し、そのデータに基づいてトランザクションを準備します。

更新トランザクションがユーザ入力によって定義されると、そのデータはタイムスタンプと共に単一の送信として送られます。これにより、そのデータが最初に取り出したデータと同じであることを確認できます。トランザクションが正常に実行されると、変更されたデータのタイムスタンプが更新されます。別のユーザからの更新トランザクションによって現在のトランザクションが処理するデータが変更された場合、タイムスタンプが変更され、現在のトランザクションは拒否されます。ほとんどの場合、関連データが変更されることはないので通常トランザクションは正常に実行されます。あるトランザクションが失敗すると、アプリケーションは更新されたデータをリフレッシュし、必要に応じてトランザクションを再作成するよう通知します。

6. 同位置更新を使用する

データ行の同位置での更新は、行の移動より非常に高速です。行の移動は、更新処理でテーブル設計の許容範囲を越えるスペースが必要な場合に行う必要があります。必要なスペースを持つ行を最初から設計しておけば、更新は早くなります。ただし、テーブルに必要なディスク空間は大きくなります。ディスク スペースのコストは低いので、パフォーマンスが向上するのであれば、使用量を少しだけ増やすことは価値ある投資だと言えるでしょう。

7. 操作データをできるだけ小さくする

アプリケーションによっては、操作データを履歴データと同じテーブルに格納するものもあります。時間の経過と共に履歴データが蓄積されていくと、すべての操作クエリでは、新しいデータを取得するために (日々の作業では) 役に立たないデータを大量に読み取らなければなりません。これを回避するには、過去のデータを別のテーブルに移動し、まれにしか発生しない履歴クエリのためにこれらのテーブルを結合します。これを行うことができない場合、最も頻繁に使用されるデータが論理的および物理的に配置されるよう、テーブルをインデックス処理およびクラスタ化します。

8. パイプラインと並行処理を使用する

DBMS は、さまざまな作業を大量に処理するときに最も能力を発揮します。DBMS の最も不適切な使い方は、1 つの大規模なシングル スレッド アプリケーション用のダム ファイル ストレージとして使用することです。容易に区別できる作業サブセットを扱う大量の並行処理をサポートするようアプリケーションとデータを設計すれば、そのアプリケーションはより高速になります。処理に複数のステップがある場合、先行ステップが完了するまで次のステップを待つのではなく、いずれかの先行ステップが処理を終えた部分のデータに対して後続ステップが処理を開始できるようにアプリケーションを設計します。これは常に可能であるとは限りませんが、このことに留意してプログラムを設計すると、パフォーマンスを大幅に向上させることができます。

 

フッタのナビゲーションのスキップ  ページの先頭 前 次