事例に学ぶWebシステム開発のワンポイント(11)
JDBC接続を高速化する
− PreparedStatementキャッシュの威力−


株式会社NTTデータ
基盤システム事業部
池田貴之
2003/4/18
本連載では、現場でのエンジニアの経験から得られた、アプリケーション・サーバをベースとしたWebシステム開発における注意点やヒントについて解説する。巷のドキュメントではなかなか得られない貴重なノウハウが散りばめられている。読者の問題解決や今後システムを開発する際の参考として大いに活用していただきたい(編集部) 。
今回のワンポイント
PreparedStatementを利用すると、JDBC接続の性能が向上することはよく知られている。アプリケーションサーバによっては、「PreparedStatementキャッシュ」という機能を持っており、さらに性能向上を図ることができる。今回は、この機能の仕組みと効果について紹介していく。

本記事は2003年に執筆されたものです。Java高速化・チューニング全般の最新情報は@IT キーワードINDEXの「Javaパフォーマンス管理」をご参照ください。

PreparedStatementとは

今回の内容

PreparedStatementとは
PreparedStatementとWebアプリケーション
APサーバのPreparedStatementキャッシュ機能

PreparedStatementキャッシュの効果
性能が向上した事例

 PreparedStatementキャッシュの前に、まずPreparedStatementについて説明する。

 PreparedStatementは、その名のとおり、JDBCのステートメント(SQL文)を前もって準備(プリペア)しておくことによって、性能を向上させる機能である。 以下に、通常のStatementを用いたソースと、PreparedStatementを用いたソースの例を示す。

リスト1 Statementを利用したJDBC接続
Statement stmt = conn.createStatement();
for ( int id = 0 ; id < 10000 ; id++ ) {
  String sql = "SELECT NAME FROM ITEM WHERE I_ID = " + id;
  ResultSet rs = stmt.executeQuery(sql);
  while ( rs.next() ) {
    // 表示などの処理
  }
}

リスト2 PreparedStatementを利用したJDBC接続
String sql = "SELECT NAME FROM ITEM WHERE I_ID = ?";
PreparedStatement ps = conn.prepareStatement(sql);
  for ( int id = 0 ; id < 10000 ; id++ ) {
  ps.setInt(1,id);
  ResultSet rs = ps.executeQuery();
  while ( rs.next() ) {
    // 表示などの処理
  }
}

 PreparedStatementでは、WHERE句の中の動的に変わる部分を、プレースホルダー「?(疑問符)」としておき、実行時にsetXXX()メソッドを使ってここに値を渡す。StetementおよびPreparedStatementを利用した場合のそれぞれの処理フローを図1に示す。

図1 StatementとPreparedStatementの処理フロー

 Statementを用いた場合、SQL文の解析および実行は、executeQuery()メソッドを実行したときに行われる(createStatement()メソッドでは行われない)。一方、PreparedStatementを用いた場合、prepareStatement()メソッドで解析が行われ、executeQuery()メソッドで実行が行われる。

 パラメータ部の値のみが変化するSQLの場合、PreparedStatementを用いることで、繰り返し実行時のプリペア処理を省くことになり、性能が向上するのである。

 では、ここで上記2つのソースの性能を比較してみる。

実行時間
Statementを利用した場合 29103ミリ秒
PreparedStatementを利用した場合 11357ミリ秒
表1 StatementとPreparedStatementを利用した場合の性能

 このように、PreparedStatementを用いた場合、大幅に性能が向上していることが分かる。

PreparedStatementとWebアプリケーション

 さて、先ほどの例では、大きく性能は向上した。しかし、Webアプリケーション上で実際にPreparedStatementを利用した場合には、ここまでは性能は向上しないのである。それはなぜだろうか。上記のプログラムの場合、1回のプリペア(java.sql.ConnectionクラスのprepareStatement()メソッド)の後、同じPreparedStatementを何度も(ここでは1万回)使い回している。そして、最後に1回だけクローズしている。

 しかしわれわれが普通のWebアプリケーションを作成する場合、1回のプリペアの後、そのPreparedStatementを1万回も実行するだろうか。だいたい1回使った後、クローズしてしまうはずである(図2の説明1)。すなわち、1回JSPやサーブレットが呼ばれるたびに、PreparedStatementが作成され、クローズされ、破棄されているわけである。

 確かに、毎回同一のSQLを利用するので、データベース側のキャッシュにはヒットしやすい(図2の説明2)。それにしても、それほど大きな効果は得られないことになる。

図2 PreparedStatementとWebアプリケーション

 このように、WebアプリケーションでPreparedStatementを用いた場合、それほどの性能向上は望めない。しかし、アプリケーションサーバによっては、キャッシュを用いて性能向上を行う機能が用意されている。以下では、その機能を紹介していく。

 
APサーバのPreparedStatementキャッシュ機能

 アプリケーションサーバによっては、「PreparedStatementキャッシュ」という機能が用意されている。PreparedStatementキャッシュは、一度作成されたPreparedStatementを、クローズせずにアプリケーションサーバのメモリ上にキャッシュしておく。同じSQLが来た場合、キャッシュ上のPreparedStatementが再利用されることになる。すなわち、プリペア作業は行われないことになる。通常のWebアプリケーションの場合、同じSQLが何度も使われることが多いので、これにより性能向上が期待できるのである。図3に仕組みを示す。

図3 PreparedStatementキャッシュの仕組み (クリックすると拡大します)

PreparedStatementキャッシュの効果

 PreparedStatementキャッシュを利用した場合、以下の効果が予測できる。

1.Javaのオブジェクト生成コストの削減
オブジェクト生成のコストは意外に高い。キャッシュを利用することで、オブジェクト生成回数を減らすことができる。また同様に破棄されるオブジェクト数も減少するので、ガーベジコレクションが発生しにくくなるというメリットもある。これにより、アプリケーションサーバのCPU使用率を低減させることができる。

2.データベースとの通信回数の削減
プリペア時にはSQLをデータベースに送信するが、プリペアは行われないので、その分通信回数が減っている。これにより、アプリケーションサーバ、データベースサーバのCPU使用率および、ネットワーク使用率を低減させることができる。

3.データベース上でのParse回数削減
プリペア時にSQL解析を実施してしまっているので、解析は行われなくなる。これにより、データベースサーバのCPU使用率を低減させることができる。


図4 PreparedStatementキャッシュの効果
 
性能が向上した事例

 では、実際にWebアプリケーションを使って測定した結果、性能が向上した事例を紹介する。まず、このシステムの構成を以下に挙げる。

図5 システム構成

 このシステムにおいて、PreparedStatementキャッシュ有効と無効でそれぞれ性能を測定した。なお、性能比較は、処理時間ではなく、スループットで行っている。

スループット(PreparedStatementキャッシュ無効の場合のスループットを100としたときの値)
PreparedStatementキャッシュ無効 100
PreparedStatementキャッシュ有効 130
表2 スループット比較

 表2のとおり、PreparedStatementキャッシュを有効にした場合、30%のスループット向上効果が得られた。

 以上のように、PreparedStatementキャッシュを用いることで、Webアプリケーションの性能向上を図ることができる。この機能を積極的に使ってもらいたい。


INDEX
事例に学ぶWebシステム開発のワンポイント
  第1回 クラスタ化すると遅くなる?(2002/3/9)
  第2回 キャッシュが性能劣化をもたらす“なぞ”を解く(2002/3/23)
  第3回 クラスタは何台までOK?(2002/4/19)
  第4回 マルチスレッドのいたずらに注意(2002/5/14)
  第5回 サービス中にアプリケーションを入れ替える(2002/6/4)
  第6回 APサーバからの応答がなくなった、なぜ?(2002/11/30)
  第7回 低負荷なのにCPU使用率が100%?(2002/12/11)
  第8回 文字化け“???”の法則とその防止策(2003/1/28)
  第9回 メモリは足りているのに“OutOfMemory”(2003/2/15)
  第10回 レスポンスキャッシュでパフォーマンス向上(2003/3/29)
第11回 JDBC接続を高速化する(2003/4/18)
  第12回 ブラウザキャッシュでパフォーマンス向上(2003/5/10)
  第13回 ファイルアップロード/ダウンロードに潜むわな(2003/6/12)


筆者プロフィール
池田 貴之(いけだ たかゆき)

現在、株式会社NTTデータビジネス開発事業本部に所属。 技術支援グループとして、J2EEをベースにしたWebシステム開発プロジェクトを対象に、技術サポートを行っている。特に、性能・信頼性といった方式技術を中心に活動中。


Java Solution全記事一覧





TechTargetジャパン

Java Solution フォーラム 新着記事

@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

RSSフィード

キャリアアップ

- PR -
@IT Sepcial

イベントカレンダー

PickUpイベント

- PR -
もっと見る
- PR -

お勧め求人情報

ホワイトペーパーTechTargetジャパン

@IT Sepcial
ソリューションFLASH