私は現在、SQL データベース内の多数のデータ レコードを管理する Java(7) で実装されたデスクトップ アプリケーションに取り組んでいます。テーブルは数個しかありませんが、多数のレコードが含まれています。1 つのテーブルに対して複雑なクエリを実行する必要がありますが、複雑な結合操作は必要ありません。
これまでのところ、私はpostgresで作業してきました。しかし、これはデスクトップのシングルユーザー アプリケーションであるため、sqlite の使用についても検討しました (言うまでもなく、セットアップの複雑さも軽減されます)。そこで、いくつかのパフォーマンス テストを行うために、単純な Python スクリプトを作成しました。私が驚いたのは、まず、sqlite が実際にどれだけうまく機能したか、そして次に、Python ではクエリの応答時間が Java よりもはるかに短かったことです。
一般的なシナリオは、ID のリストに従ってレコードのバッチを選択することです。Python では、次のコードを使用して応答時間をテストしました。
rand_selection = ','.join([str(int(random.random()* MAX_INDEX )) for i in xrange(PAGE_SIZE)])
start = time.time();
c = db.cursor();
res = c.execute("SELECT * FROM bigtable WHERE id in ("+rand_selection+")");
reslist = [str(t) for t in res]; c.close();
print( time.time() - start );
これにより、MAX_INDEX=111000 および PAGE_SIZE=100 に対して約 5 ミリ秒のデルタが得られます。
まあ、素晴らしい。それでは、Java に移りましょう。私はjdbc-sqliteドライバーを使用しています。まったく同じテーブルに対してまったく同じクエリを実行しましたが、クエリ時間は常に約 200 ミリ秒であり、私のユース ケースでは受け入れられません。
私は何かを見逃していますか?
これは非常に一般的な質問であることは承知しています。しかし、誰かが jdbc-sqlite の経験があり、何が起こっているのかを経験から知っているかもしれません…</p>
[編集] : 提案どおりに timit.default_timer() を使用すると (ありがとう、Martijn Pieters)、同様の結果が得られます。
[Edit2] : CL の提案に従って、Java コードの簡易版を書きました。このコードを使用して結果を確認することはできず、応答時間は Python コードの場合とほぼ同じでした。ただし、これは別の jdk (openjdk7 と oracle jdk7) を使用して別のマシンでテストしました。確かに、私の他のテスト コードにはおそらくいくつかの問題があります。
[編集 2013-08-16] : 元のセットアップを使用して同じテストを実行しました。また、postgres と比較しました。
Model Name: MacBook Pro
Model Identifier: MacBookPro5,5
Processor Name: Intel Core 2 Duo
Processor Speed: 2.53 GHz
Memory: 8GB
OS-Version: 10.8.4
Java:
Java(TM) SE Runtime Environment (build 1.7.0_21-b12)
Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode)
テストコード (ずさんなコーディングをお許しください…):
package ch.dsd;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
public class Main {
private static int COL_COUNT = 20;
private static int TESTRUNS = 20;
private static int INDEX_COUNT = 64;
/*
CREATE TABLE bigtable ( id INTEGER PRIMARY KEY ASC, prop0 real, prop1 real, ... , prop19 real );
*/
static class Entity {
private long id;
private ArrayList<Double> properties = new ArrayList<Double>(COL_COUNT);
public Entity() {
for( int i = 0; i < COL_COUNT; i++) {
properties.add(0.0);
}
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public void setProperty(int idx, double prop) {
properties.set(idx, prop);
}
public double getProperty(int idx) {
return properties.get(idx);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for( double prop: properties ) {
sb.append(prop);
sb.append(",");
}
sb.delete(sb.length()-1, sb.length());
return sb.toString();
}
}
private static String placeholders( int n ) {
StringBuilder sb = new StringBuilder();
if( n > 0 ) {
sb.append("?");
for( int i = 1; i < n; i++ )
sb.append(",?");
return sb.toString();
}
return "";
}
private static void setRandomIdcs( PreparedStatement ps, int start, int stop, int max ) throws SQLException {
for( int i = start; i <= stop; i++ ) {
ps.setLong(i, (long) ((double) max * Math.random()));
}
}
private static void setRandomValues( PreparedStatement ps, int start, int stop ) throws SQLException {
for( int i = start; i <= stop; i++ ) {
ps.setDouble(i, Math.random());
}
}
private static void readFromResultSet( ResultSet rs, List<Entity> lst ) throws SQLException {
while(rs.next()) {
final Entity e = new Entity();
e.setId(rs.getLong(1));
for( int i = 0; i < COL_COUNT; i++ )
e.setProperty(i, rs.getDouble(i+2));
lst.add(e);
}
}
public static void performTest(Connection c) throws SQLException {
final PreparedStatement ps = c.prepareStatement("SELECT * FROM bigtable WHERE id in ("+placeholders(INDEX_COUNT)+")");
ArrayList<Entity> entities = new ArrayList<Entity>();
for( int i = 0; i < TESTRUNS; i++ ) {
setRandomIdcs( ps, 1, INDEX_COUNT, 1000000 ); // there are one million entries stored in the test table
long start = System.currentTimeMillis();
final ResultSet rs = ps.executeQuery();
readFromResultSet(rs, entities);
// System.out.println(entities.get(INDEX_COUNT-1));
System.out.println("Time used:" + (System.currentTimeMillis() - start));
System.out.println("Items read:" + entities.size());
rs.close();
entities.clear();
}
ps.close();
}
public static void createPSQLTable(Connection c) throws SQLException {
final String create_stmt = "CREATE TABLE IF NOT EXISTS bigtable (id SERIAL PRIMARY KEY, " +
"prop0 double precision,prop1 double precision,prop2 double precision,prop3 double precision,prop4 double precision,prop5 double precision,prop6 double precision,prop7 double precision,prop8 double precision,prop9 double precision,prop10 double precision,prop11 double precision,prop12 double precision,prop13 double precision,prop14 double precision,prop15 double precision,prop16 double precision,prop17 double precision,prop18 double precision,prop19 double precision)";
final PreparedStatement ps = c.prepareStatement(create_stmt);
ps.executeUpdate();
ps.close();
}
public static void loadPSQLTable( Connection c ) throws SQLException {
final String insert_stmt = "INSERT INTO bigtable VALUES (default, " + placeholders(20) + ")";
final PreparedStatement ps = c.prepareStatement(insert_stmt);
for( int i = 0; i < 1000000; i++ ) {
setRandomValues(ps, 1, 20);
ps.executeUpdate();
}
c.commit();
}
public static void main(String[] args) {
Connection c = null;
try {
Class.forName("org.sqlite.JDBC");
c = DriverManager.getConnection("jdbc:sqlite:/Users/dsd/tmp/sqlitetest/testdb.db");
c.setAutoCommit(false);
performTest(c);
c.close();
System.out.println("POSTGRES");
System.out.println("========");
final Properties props = new Properties();
props.setProperty("user", "dsd");
c = DriverManager.getConnection("jdbc:postgresql:testdb", props);
c.setAutoCommit(false);
createPSQLTable(c);
// loadPSQLTable(c);
performTest(c);
c.close();
} catch ( Exception e ) {
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
System.exit(0);
}
}
}
結果:
Time used:348
Items read:64
Time used:407
Items read:64
Time used:259
Items read:64
Time used:341
Items read:64
Time used:325
Items read:64
Time used:145
Items read:64
Time used:70
Items read:64
Time used:98
Items read:64
Time used:91
Items read:64
Time used:134
Items read:64
Time used:68
Items read:64
Time used:51
Items read:64
Time used:51
Items read:64
Time used:51
Items read:64
Time used:55
Items read:64
Time used:67
Items read:64
Time used:56
Items read:64
Time used:90
Items read:64
Time used:56
Items read:64
Time used:51
Items read:64
POSTGRES
========
Time used:75
Items read:64
Time used:58
Items read:64
Time used:31
Items read:64
Time used:26
Items read:64
Time used:34
Items read:64
Time used:6
Items read:64
Time used:5
Items read:64
Time used:4
Items read:64
Time used:5
Items read:64
Time used:6
Items read:64
Time used:5
Items read:64
Time used:6
Items read:64
Time used:4
Items read:64
Time used:28
Items read:64
Time used:3
Items read:64
Time used:4
Items read:64
Time used:4
Items read:64
Time used:4
Items read:64
Time used:3
Items read:64
Time used:5
Items read:64