- PR -

java.sql.Statementとjava.sql.PreparedStatementについての性能テスト

1
投稿者投稿内容
Manoaman
会議室デビュー日: 2006/11/04
投稿数: 8
投稿日時: 2006-11-19 13:58
java.sql.Statement と java.sql.PreparedStatementについて性能テストをしているのですが、どうも思った結果が出ずに困っております。私の予想では、PreparedStatementの実行時間がStatementの1/3程度になると期待していたのですが、Statementの方が早く実行できている状況です。

もしテストの仕方、ミスコーディングがありましたらご指摘いただけないでしょうか?どうぞよろしくお願いします。

[テスト条件]

MySQLのテーブルに10,000件のレコードがあり、1000回のSELECTクエリを10セットプログラムから行う。StatementとPreparedStatementクラスそれぞれに対して実施する。

[ソース]

/**
* Simple select preformance test.
*/
public void select() {
// Get connection and create statement.
Connection con = getConnection();
Statement stmt = null;
PreparedStatement pstmt = null;
int numberOfSQL = 1000;
int numberOfTests = 10;
long startTime = 0;
long endTime = 0;
long totalTime = 0;
float averageTime = 0.0F;
try {
// Statement Test.
System.out.println("Test Statement...");
for(int j=0;j<numberOfTests;j++){
startTime = System.currentTimeMillis();
for(int i=0;i<numberOfSQL;i++){
String sql1 = "SELECT id FROM table WHERE value2="+i;
stmt = con.createStatement();
stmt.executeQuery(sql1);
}
endTime = System.currentTimeMillis();
System.out.println("Time: " + (endTime-startTime) + " miliseconds");
totalTime += (endTime-startTime);
}
averageTime = (float)(totalTime/numberOfTests);
System.out.println("Total Time: " + totalTime + " miliseconds");
System.out.println("Average Time: " + averageTime + " miliseconds");

// PreparedStatement Test.
totalTime = 0;
System.out.println("Test PreparedStatement...");
for(int j=0;j<numberOfTests;j++){
startTime = System.currentTimeMillis();
String sql2 = "SELECT id FROM table WHERE value2=?";
pstmt = con.prepareStatement(sql2);
for(int i=0;i<numberOfSQL;i++){
pstmt.setInt(1,i);
pstmt.executeQuery();
}
endTime = System.currentTimeMillis();
System.out.println("Time: " + (endTime-startTime) + " miliseconds");
totalTime += (endTime-startTime);
}
averageTime = (float)(totalTime/numberOfTests);
System.out.println("Total Time: " + totalTime + " miliseconds");
System.out.println("Average Time: " + averageTime + " miliseconds");

} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(stmt!=null) stmt.close();
if(pstmt!=null) pstmt.close();
if(con!=null) con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}


[結果]

Test Statement...
Time: 5596 miliseconds
Time: 230 miliseconds
Time: 144 miliseconds
Time: 100 miliseconds
Time: 139 miliseconds
Time: 100 miliseconds
Time: 95 miliseconds
Time: 161 miliseconds
Time: 92 miliseconds
Time: 98 miliseconds
Total Time: 6755 miliseconds
Average Time: 675.0 miliseconds

Test PreparedStatement...
Time: 5398 miliseconds
Time: 5220 miliseconds
Time: 5186 miliseconds
Time: 5201 miliseconds
Time: 5175 miliseconds
Time: 5201 miliseconds
Time: 5189 miliseconds
Time: 5171 miliseconds
Time: 5190 miliseconds
Time: 5181 miliseconds
Total Time: 52112 miliseconds
Average Time: 5211.0 miliseconds
Manoaman
会議室デビュー日: 2006/11/04
投稿数: 8
投稿日時: 2006-11-19 13:59
すみません、ソースを[code]タグで囲むのを忘れてしまいました。。

コード:
/**
* Simple select preformance test.
*/
public void select() {
	// Get connection and create statement.
	Connection con = getConnection();
	Statement stmt = null;
	PreparedStatement pstmt = null;
	int numberOfSQL = 1000;
	int numberOfTests = 10;
	long startTime = 0;
	long endTime = 0;
	long totalTime = 0;
	float averageTime = 0.0F;
	try {
		// Statement Test.
		System.out.println("Test Statement...");
		for(int j=0;j<numberOfTests;j++){
			startTime = System.currentTimeMillis();
			for(int i=0;i<numberOfSQL;i++){
				String sql1 = "SELECT id FROM table WHERE value2="+i;
				stmt = con.createStatement();
				stmt.executeQuery(sql1);
		}
		endTime = System.currentTimeMillis();
		System.out.println("Time: " + (endTime-startTime) + " miliseconds");
		totalTime += (endTime-startTime);
	}
	averageTime = (float)(totalTime/numberOfTests);
	System.out.println("Total Time: " + totalTime + " miliseconds");
	System.out.println("Average Time: " + averageTime + " miliseconds");

	// PreparedStatement Test.
	totalTime = 0;
	System.out.println("Test PreparedStatement...");
	for(int j=0;j<numberOfTests;j++){
		startTime = System.currentTimeMillis();
		String sql2 = "SELECT id FROM table WHERE value2=?";
		pstmt = con.prepareStatement(sql2);
		for(int i=0;i<numberOfSQL;i++){
			pstmt.setInt(1,i);
			pstmt.executeQuery();
		}
		endTime = System.currentTimeMillis();
		System.out.println("Time: " + (endTime-startTime) + " miliseconds");
		totalTime += (endTime-startTime);
	}
	averageTime = (float)(totalTime/numberOfTests);
	System.out.println("Total Time: " + totalTime + " miliseconds");
	System.out.println("Average Time: " + averageTime + " miliseconds");

	} catch (Exception e) {
		e.printStackTrace();
	} finally {
	try {
		if(stmt!=null) stmt.close();
		if(pstmt!=null) pstmt.close();
		if(con!=null) con.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}


nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2006-11-19 14:10
気になったのはStatement Test.の箇所でstmt.executeQuery()を
実行した後に結果の取得をしていませんが、ResultSetで結果を取得した
際もおなじようなパフォーマンスでしょうか?

MySQLのJDBCドライバの実装がどうなっているかは知りませんが、
データを遅延ロードするようになっていたら正しく計測されませんよね。
とりあえず思いついただけのネタなのではずしているかもしれませんが。

# なんとなくインデントがずれていますね…。
nekoyama
ベテラン
会議室デビュー日: 2005/03/12
投稿数: 71
投稿日時: 2006-11-19 14:45
"PreparedStatementの実行時間がStatementの1/3程度になると期待"の根拠は
なんですか?

JDBCドライバの実装に依存すると思いますが、
for文中でPreparedStatementを生成、破棄しても、
Statementより高速になるような実装なのか?と思います。

for文中で何度も生成せずに、for文の外でPreparedStatementを
一度だけ生成して、それを使いまわしたら、結果が変わるかも。

後は、nagiseさんと同意です。

uk
ぬし
会議室デビュー日: 2003/05/20
投稿数: 1155
お住まい・勤務地: 東京都
投稿日時: 2006-11-20 10:52
Statementクラスを使った結果が2回目以降格段によくなっているのは、おそらく
データキャッシュ、ステートメントキャッシュのどちらかあるいは両方が効いている
からでしょうね。

興味深いのは、PreparedStatementの結果ですね。MySQLかJDBCドライバの実装に問題
がある可能性がありますが、どのような環境で実行したのでしょうか。
progman
大ベテラン
会議室デビュー日: 2005/06/08
投稿数: 227
投稿日時: 2006-11-20 13:09
問い合わせ毎にstmt,pstmtのクローズが必要ではないですか?
あしゅ
ぬし
会議室デビュー日: 2005/08/05
投稿数: 613
投稿日時: 2006-11-20 17:56
とりあえず、PreparedStatementを使って速くなる要素とは、

1. パラメータを利用してPreparedStatementを使いまわす
2. JDBCドライバがSQLの解析結果のキャッシュに対応している
3. Batch Updateを利用して複数行の更新をまとめて行える

といったところだと思うのですが。

少なくとも2と3はJDBCドライバ/DBの双方が対応している必要があります。

接続プールがPreparedStatementをキャッシュするケースもありますが、
Statementの置き換えとして使えば、多少なりともSQLの解析が入るので、
PreparedStatementの方が速くなる可能性は極めて低いでしょう。

この辺りをきちんとやらない限り、ベンチマークする意味なんてないのでは?
きちんとやっても特定の状況や環境での計測結果に過ぎないです。

間違いだらけのベンチマークは害しか生まないですよ。
1

スキルアップ/キャリアアップ(JOB@IT)