本連載では、現場でのエンジニアの経験から得られた、アプリケーション・サーバをベースとしたWebシステム開発における注意点やヒントについて解説する。巷のドキュメントではなかなか得られない貴重なノウハウが散りばめられている。読者の問題解決や今後システムを開発する際の参考として大いに活用していただきたい。(編集局)
PreparedStatementを利用すると、JDBC接続の性能が向上することはよく知られている。アプリケーションサーバによっては、「PreparedStatementキャッシュ」という機能を持っており、さらに性能向上を図ることができる。今回は、この機能の仕組みと効果について紹介していく。
本記事は2003年に執筆されたものです。Java高速化・チューニング全般の最新情報は@IT キーワードINDEXの「Javaパフォーマンス管理」をご参照ください。
PreparedStatementキャッシュの前に、まずPreparedStatementについて説明する。
PreparedStatementは、その名のとおり、JDBCのステートメント(SQL文)を前もって準備(プリペア)しておくことによって、性能を向上させる機能である。 以下に、通常のStatementを用いたソースと、PreparedStatementを用いたソースの例を示す。
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() ) { // 表示などの処理 } }
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に示す。
Statementを用いた場合、SQL文の解析および実行は、executeQuery()メソッドを実行したときに行われる(createStatement()メソッドでは行われない)。一方、PreparedStatementを用いた場合、prepareStatement()メソッドで解析が行われ、executeQuery()メソッドで実行が行われる。
パラメータ部の値のみが変化するSQLの場合、PreparedStatementを用いることで、繰り返し実行時のプリペア処理を省くことになり、性能が向上するのである。
では、ここで上記2つのソースの性能を比較してみる。
実行時間 | |
---|---|
Statementを利用した場合 | 29103ミリ秒 |
PreparedStatementを利用した場合 | 11357ミリ秒 |
表1 StatementとPreparedStatementを利用した場合の性能 |
このように、PreparedStatementを用いた場合、大幅に性能が向上していることが分かる。
さて、先ほどの例では、大きく性能は向上した。しかし、Webアプリケーション上で実際にPreparedStatementを利用した場合には、ここまでは性能は向上しないのである。それはなぜだろうか。上記のプログラムの場合、1回のプリペア(java.sql.ConnectionクラスのprepareStatement()メソッド)の後、同じPreparedStatementを何度も(ここでは1万回)使い回している。そして、最後に1回だけクローズしている。
しかしわれわれが普通のWebアプリケーションを作成する場合、1回のプリペアの後、そのPreparedStatementを1万回も実行するだろうか。だいたい1回使った後、クローズしてしまうはずである(図2の説明1)。すなわち、1回JSPやサーブレットが呼ばれるたびに、PreparedStatementが作成され、クローズされ、破棄されているわけである。
確かに、毎回同一のSQLを利用するので、データベース側のキャッシュにはヒットしやすい(図2の説明2)。それにしても、それほど大きな効果は得られないことになる。
このように、WebアプリケーションでPreparedStatementを用いた場合、それほどの性能向上は望めない。しかし、アプリケーションサーバによっては、キャッシュを用いて性能向上を行う機能が用意されている。以下では、その機能を紹介していく。
アプリケーションサーバによっては、「PreparedStatementキャッシュ」という機能が用意されている。PreparedStatementキャッシュは、一度作成されたPreparedStatementを、クローズせずにアプリケーションサーバのメモリ上にキャッシュしておく。同じSQLが来た場合、キャッシュ上のPreparedStatementが再利用されることになる。すなわち、プリペア作業は行われないことになる。通常のWebアプリケーションの場合、同じSQLが何度も使われることが多いので、これにより性能向上が期待できるのである。図3に仕組みを示す。
PreparedStatementキャッシュを利用した場合、以下の効果が予測できる。
1.Javaのオブジェクト生成コストの削減
オブジェクト生成のコストは意外に高い。キャッシュを利用することで、オブジェクト生成回数を減らすことができる。また同様に破棄されるオブジェクト数も減少するので、ガーベジコレクションが発生しにくくなるというメリットもある。これにより、アプリケーションサーバのCPU使用率を低減させることができる。
2.データベースとの通信回数の削減
プリペア時にはSQLをデータベースに送信するが、プリペアは行われないので、その分通信回数が減っている。これにより、アプリケーションサーバ、データベースサーバのCPU使用率および、ネットワーク使用率を低減させることができる。
3.データベース上でのParse回数削減
プリペア時にSQL解析を実施してしまっているので、解析は行われなくなる。これにより、データベースサーバのCPU使用率を低減させることができる。
では、実際にWebアプリケーションを使って測定した結果、性能が向上した事例を紹介する。まず、このシステムの構成を以下に挙げる。
このシステムにおいて、PreparedStatementキャッシュ有効と無効でそれぞれ性能を測定した。なお、性能比較は、処理時間ではなく、スループットで行っている。
スループット(PreparedStatementキャッシュ無効の場合のスループットを100としたときの値) | |
---|---|
PreparedStatementキャッシュ無効 | 100 |
PreparedStatementキャッシュ有効 | 130 |
表2 スループット比較 |
表2のとおり、PreparedStatementキャッシュを有効にした場合、30%のスループット向上効果が得られた。
以上のように、PreparedStatementキャッシュを用いることで、Webアプリケーションの性能向上を図ることができる。この機能を積極的に使ってもらいたい。
入内島正之
現在、株式会社NTTデータビジネス開発事業本部に所属。 技術支援グループとして、J2EEをベースにしたWebシステム開発プロジェクトを対象に、技術サポートを行っている。特に、性能・信頼性といった方式技術を中心に活動中。
Copyright © ITmedia, Inc. All Rights Reserved.