SQLite:java/JDBCSQLite和python/sqlite3之间的区别

SQLite:java/JDBCSQLite和python/sqlite3之间的区别,java,python,jdbc,sqlite,Java,Python,Jdbc,Sqlite,我目前正在开发用java(7)实现的桌面应用程序,该应用程序管理sql数据库中的大量数据记录。只有几个表,但它们包含大量的记录。我需要在单个表上执行复杂的查询,但不需要复杂的联接操作 到目前为止,我一直在为博士后工作。但由于它是一个桌面单用户应用程序,我也考虑过使用sqlite(不用说,这也会降低设置的复杂性)。因此,我编写了一个简单的python脚本,以便进行一些性能测试。让我惊讶的是,首先,sqlite的实际性能如何,其次,python中的查询响应时间比java中的小得多 一个常见的场景是根

我目前正在开发用java(7)实现的桌面应用程序,该应用程序管理sql数据库中的大量数据记录。只有几个表,但它们包含大量的记录。我需要在单个表上执行复杂的查询,但不需要复杂的联接操作

到目前为止,我一直在为博士后工作。但由于它是一个桌面单用户应用程序,我也考虑过使用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,我得到了大约5ms的增量

很好。现在,让我们转到java:我使用驱动程序。我在完全相同的表上执行了完全相同的查询,查询时间总是在200毫秒左右,这对于我的用例来说是不可接受的

我是不是错过了什么

我知道这是一个非常普遍的问题。但也许有人对JDBCSQLite有一定的经验,并从经验中知道发生了什么

[编辑]:按照建议使用timit.default\u timer()(谢谢,Martijn Pieters)会得到类似的结果

[Edit2]:根据CL的建议,我编写了java代码的简化版本。使用这段代码,我无法验证结果,响应时间与python代码大致相同。但是,我使用不同的jdk(openjdk7与oracle jdk7)在不同的机器上测试了这一点。诚然,我的其他测试代码很可能存在一些问题

[编辑2013-08-16]:我现在使用原始设置执行了相同的测试。我还把它和博士后做了比较

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)
测试代码(请原谅粗心的编码…):


Python是用C编写的,并且具有同样用C编写的“sqlite”可执行文件,链接在中

不存在数据封送,也不存在格式之间的转换,因为Python和底层sqlite库都使用相同的数据类型和编码,而这些数据类型和编码与编译它们的平台无关

另一方面,Java(JVM也是用C编写的,但是……)使用特定的与平台无关的数据类型,特别是所有字符串都是unicode。为了与底层sqlite可执行文件通信,java库必须使用JNI,它(通常)涉及一些数据类型转换和字符编码。当将C字符串转换为unicode并再次转换时,这可能非常占用cpu

说到这里,我已经多次使用SQLiteJDBCJAR,但从未真正注意到任何性能问题


您可以尝试将JavaDB(aka.Derby)视为嵌入式Java数据库。它是用纯Java编写的,使用“本机”Java编码,并且是“零维护”

Python是用C编写的,它的“sqlite”可执行文件也是用C编写的,链接在中

不存在数据封送,也不存在格式之间的转换,因为Python和底层sqlite库都使用相同的数据类型和编码,而这些数据类型和编码与编译它们的平台无关

另一方面,Java(JVM也是用C编写的,但是……)使用特定的与平台无关的数据类型,特别是所有字符串都是unicode。为了与底层sqlite可执行文件通信,java库必须使用JNI,它(通常)涉及一些数据类型转换和字符编码。当将C字符串转换为unicode并再次转换时,这可能非常占用cpu

说到这里,我已经多次使用SQLiteJDBCJAR,但从未真正注意到任何性能问题


您可以尝试将JavaDB(aka.Derby)视为嵌入式Java数据库。它是用纯Java编写的,使用“本机”Java编码,并且是“零维护”

请注意,根据您的平台,使用
time.time()
可能不准确。改为使用
timeit.default\u timer()
,或者最好改为使用
timeit
模块执行时间代码。您将包括构建结果数组的时间。请显示Java代码。这与您的Postgress性能相比如何?请注意,根据您的平台,使用
time.time()
可能不准确。改为使用
timeit.default\u timer()
,或者最好改为使用
timeit
模块执行时间代码。您将包括构建结果数组的时间。请展示Java代码。这与您的Postgress性能相比如何?原则上,我同意您的意见。可以肯定的是,必须使用探查器进行检查。不幸的是,我没有时间做一些深入的分析。然而,如果我错了,请纠正我,但是转换不应该在我的示例中起主要作用,因为我在处理double和int,而不是字符串。此外,我猜转换对于其他数据库驱动程序(如postgres、mysql等)也是一个问题。原则上,我同意你的意见。可以肯定的是,必须使用探查器进行检查。不幸的是,我没有时间做一些深入的分析。然而,如果我错了,请纠正我,但是转换不应该在我的示例中起主要作用,因为我在处理double和int,而不是字符串。此外,我猜转换对于其他数据库驱动程序(如postgres、mysql等)也是一个问题。
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