Java 为什么SQLite在JDBC中速度如此之慢?

Java 为什么SQLite在JDBC中速度如此之慢?,java,sqlite,jdbc,Java,Sqlite,Jdbc,A读了很多东西,比如: 但我不知道问题出在哪里。 情况是,我有一个SQLite数据库(来自Android平板电脑)和一个不太大的表(其中约有50000行) 例如,如果在Sqlite管理器中运行“select*from table”,则需要0.11秒,正确 但是。。。如果我在Java程序(使用SQLite JDBC)中执行此操作,则需要20分钟!!!不是开玩笑 有人(某处)说这取决于版本。 但我的问题是怎么做 因为此命令:“SELECT sqlite_version()”在不同情况下对同一.db

A读了很多东西,比如:

但我不知道问题出在哪里。 情况是,我有一个SQLite数据库(来自Android平板电脑)和一个不太大的表(其中约有50000行) 例如,如果在Sqlite管理器中运行“select*from table”,则需要0.11秒,正确

但是。。。如果我在Java程序(使用SQLite JDBC)中执行此操作,则需要20分钟!!!不是开玩笑

有人(某处)说这取决于版本。 但我的问题是怎么做

因为此命令:“SELECT sqlite_version()”在不同情况下对同一.db文件给出不同的结果:

  • 在一个非常旧的sqlite管理器中,它给出了3.6.19
  • 在Sqlite Studio 3.15中
  • 在sqlite.org上最新的.exe中,它给出了3.23.1 所以这不是一个与数据库相关的东西,我认为这是使用的sqlite3.exe版本
我可以整天更换JDBC驱动程序(我做了几次),但是我怎么知道我需要哪一个呢

有人有想法吗?我完全被它绊倒了

编辑: 好的,那么JDBC jar来自这里:

我的代码非常基本,起初我只是想测量速度

        Class.forName("org.sqlite.JDBC");
        Connection c1 = DriverManager.getConnection("jdbc:sqlite:" + "c:\\database.db");

        PreparedStatement stmt1 = c1.prepareStatement("select * from table1;");
        ResultSet rs = stmt1.executeQuery();
        String script = "insert into table1 values ";
        while (rs.next()) {
            script += "(";
            script += rs.getInt(1) + ", '" + rs.getString(2) + "', '" + rs.getString(3) + "'";
            script += "),";
        }
        stmt1.close();
        c1.close();

executeQuery()行需要20分钟。

在一个成功的应用程序中,我们使用
sqlite
作为数据库。在我们的应用程序中,我们还使用JPA,并将数据库定义为Java资源目录中的持久化单元:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="jpa" transaction-type="RESOURCE_LOCAL">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <properties>
            <property name="javax.persistence.jdbc.url" value="jdbc:sqlite:/ourdata/mySqliteDB.db" />
             <property name="javax.persistence.jdbc.driver" value="org.sqlite.JDBC" />
             <property name="eclipselink.logging.level" value="SEVERE"/>
             <property name="eclipselink.jdbc.cache-statements" value="true"/>
             <property name="eclipselink.weaving" value="false"/>
             <property name="eclipselink.weaving.fetch-groups" value="false"/>
             <property name="showSql" value="false"/>
        </properties>
    </persistence-unit>
</persistence>

org.eclipse.persistence.jpa.PersistenceProvider
我们没有时间用sqlite解决访问问题


在访问大型数据库表时,通常都知道需要为sql查询中使用的每一列定义一个索引,以确保快速的查询响应时间。这也适用于JPA(通常是“findall”查询)

您正在创建一个包含50k行的
字符串,这意味着您正在创建50k*5
字符串
(每个连接都会创建一个新的
字符串
实例。这会降低您的性能

while (rs.next()) {
    script += "(";
    script += rs.getInt(1) + ", '" + rs.getString(2) + "', '" + rs.getString(3) + "'";
    script += "),";
}
我注意到您不执行
字符串脚本
,因此如果您只想创建
字符串
,请使用
StringBuilder

StringBuilder script = new StringBuilder("insert into table1 values ");
    while (rs.next()) {
        script.append("(")
              .append(rs.getInt(1)).append(", '")
              .append(rs.getString(2)).append("', '")
              .append(rs.getString(3)).append("'")
          .append("),");
    }

script.setLength(script.length() - 1); //to remove the last comma.

String query = script.toString();
StringBuilder
可以防止大量的
String
实例被免费创建

如果要在此之后插入这些值,请直接使用
PreparedStatement
,而不是构建查询:

PreparedStatement psInsert = c1.prepareStatement("insert into table1 values (?,?,?)");
while (rs.next()) {
    psInsert.setInt(1, rs.getInt(1));
    psInsert.setString(2, rs.getString(2));
    psInsert.setString(2,rs.getString(3));

    psInsert.execute();
}
然后,如果您想改进这一点,请使用批处理系统发送小块插入。使用和

StringBuilder基准 不是正式的,只是一次简单的执行

LocalTime start=LocalTime.now();
StringBuilder sb=新的StringBuilder(“Foo;”);
对于(int i=0;i<50_000;i++){
附加(i)附加(;\n);
}
System.out.println(Duration.between(start,LocalTime.now()).toNanos());
字符串s=sb.toString();
System.out.println(s.substring(0,50));
这需要15纳秒

LocalTime start=LocalTime.now();
字符串s=“Foo;”;
对于(int i=0;i<50_000;i++){
s+=“行”+i+“;\n”;
}
System.out.println(Duration.between(start,LocalTime.now()).toMillis());
System.out.println(s.substring(0,50));
这需要>6秒


请添加相关的java代码,执行查询需要20分钟。这种性能上的差异肯定不是(唯一的)JDBC API相关。可能是一个特定的驱动程序错误,但您没有确切提到您使用的驱动程序,也没有提到运行JDBC逻辑的执行环境,也没有显示任何代码,这将需要复制此代码。我们没有玻璃球…我们无法知道问题是在您的代码中还是在驱动程序/库中ry/sqlite版本。您将在sqlite管理中完成的
Select
与插入到表中的代码(使用
String
串联)进行比较。首先,使用
PreparedStatement.addBatch()
插入每一行(检查是否在sqlite中管理批处理)。然后,不要连接YRU
String
。Axel,没有实际的插入,我只是构建了一个未执行的字符串。(实际上我做了,它在几秒钟内完成了,但问题是上面代码的那部分)我明白你的意思,但事实并非如此。我的计划是将此数据库传输到MySQL。如果我生成50k insert(即使在事务中)也会非常慢。正如我之前所经历的,插入MySQL的最好方法是一个insert命令,而不是所有的数据,比如(1,1,“abc”),(2,3,“abc”)…等等。所以我需要那种形式的字符串。@Rezmalac,我刚刚编辑过(在开头)演示如何使用
StringBuilder
使用较少的
String
s构建
String
查询。但这不会改变插入这些值的时间。如果您注意到大量插入时出现问题,您可以检查配置和使用的代码,因为
PreparedStatement
更适合于此(而且更安全!)@Rezmalac给你一个想法,连接100k
String
花了7秒,我们使用的是
StringBuilder
它甚至在
ms
中都无法测量。F@ck我,这似乎是愚蠢的java字符串连接。你得到了我的投票。我应该改变问题的标题吗?因为它不再相关了…@Rezmalac这真的是“愚蠢的java连接”的错误?;-)永远不要在循环中连接
String
,这是一个好规则。在每种语言中实例化都需要时间。“更改”不可变对象意味着创建一个新实例,所以这不是
String
java;)的错误好的,这是SQLite查询中的执行,“在SQLte ResultSet中构建查询”或类似的内容可能更正确,但这取决于您。
 while (rs.next()) {
    psInsert.setInt(1, rs.getInt(1));
    psInsert.setString(2, rs.getString(2));
    psInsert.setString(2,rs.getString(3));

    psInsert.addBatch();
    if(batchSize++ > 100){ //Execute every 100 rows
        psInsert.executeBatch();
        batchSize = 0;
    }
}

if(batchSize > 0){ //execute the remainings data
      psInsert.executeBatch();
}
LocalTime start = LocalTime.now();
StringBuilder sb = new StringBuilder("Foo;");
for(int i = 0; i < 50_000; i++){
    sb.append("Row").append(i).append(";\n");
}
System.out.println(Duration.between(start, LocalTime.now()).toNanos());
String s = sb.toString();
System.out.println(s.substring(0, 50));
LocalTime start = LocalTime.now();
String s = "Foo;";
for(int i = 0; i < 50_000; i++){
    s += "Row" + i + ";\n";
}
System.out.println(Duration.between(start, LocalTime.now()).toMillis());
System.out.println(s.substring(0, 50));