【问题标题】:Java ResultSet slows down SQLite In-Memory databaseJava ResultSet 减慢 SQLite In-Memory 数据库
【发布时间】:2013-10-15 16:25:15
【问题描述】:

我在使用 sqlite-jdbc 驱动程序的 Java 程序中使用 SQLite 作为 In-Memory 数据库。

我使用的查询相当复杂。然而,它非常快,通常

Class.forName("org.sqlite.JDBC");
connection = DriverManager.getConnection("jdbc:sqlite::memory:");

long startTime = System.currentTimeMillis();

Statement stmt = connection.createStatement();
stmt.setFetchSize(1000);
ResultSet rs = stmt.executeQuery(query);

System.out.println("query time " + (System.currentTimeMillis() - startTime));

ResultSetMetaData metaData = rs.getMetaData();
int columns = metaData.getColumnCount();

System.out.println("metadata time " + (System.currentTimeMillis() - startTime));

HashMap<String, String> record = new HashMap<String, String>();
while (rs.next())
{
    System.out.println("get records time " + (System.currentTimeMillis() - startTime));
    for (int i = 1; i <= columns; i++)
    {

        if (metaData.getColumnName(i) != null && rs.getObject(i) != null)
        {
            record.put(metaData.getColumnName(i), rs.getObject(i).toString());
        }
    }
    al.add(record);
}
System.out.println("all time " + (System.currentTimeMillis() - startTime));

这个小程序的输出显示了时间消耗在哪里:

query time 5
metadata time 5
get records time 5
get records time 5
get records time 6
get records time 6
get records time 15
get records time 15
get records time 26
get records time 26
all time 52

我知道如果获取大小太小,ResultSet 通常会通过网络到达数据库以移动光标并获取更多数据。但是,在我的情况下,获取大小相当大(1000),数据库在内存中,结果集包含少量条目(

有什么想法可以让它更快吗?有人知道 SQLite 如何处理选择吗?

提前致谢。 塞巴斯蒂安

【问题讨论】:

  • 较大的提取大小不一定对性能更好(例如,考虑到额外的内存分配和垃圾收集所需的),对于 sqlite 内存数据库的考虑,如网络延迟是无关紧要的。跨度>

标签: java sqlite jdbc in-memory-database


【解决方案1】:

SQLite 是一个嵌入式数据库,不做任何网络通信;它即时计算结果记录并忽略提取大小。

如果你的查询比较复杂,搜索和计算下一条记录会比较慢。

【讨论】:

  • 我不应该使用“网络”这个词。我当然知道在内存数据库的上下文中不需要网络。
  • 问题确实是查询的复杂性,最重要的是 SQLite 糟糕的执行计划。使用 CROSS JOINS(= MySQL 中的 STRAIGHT JOINS)我可以绕过这个问题。谢谢。
【解决方案2】:

for 循环非常低效。不要每次都重新检索列名,而是这样做:

ResultSetMetaData metaData = rs.getMetaData();
int columns = metaData.getColumnCount();
ArrayList<String> columnNames = new ArrayList<String>();
for (int i = 1; i <= columns; i++) {
    columnNames.add(metaData.getColumnName(i));
}

然后你可以将原来的for 循环简化为这个(这是假设你的列名不是null,它们永远不应该是):

for (int i = 0; i < columns; i++)
{
    Object cellObject = rs.getObject(columnNames.get(i));
    if( cellObject != null )
        record.put(cellObject.toString());
}

另外,我不知道你为什么每次都打电话给al.add(record);,而record 总是指向同一个HashMap(反过来每次你通过while 时都会被覆盖环形)。仔细看看你是如何使用record的。

【讨论】:

  • 感谢您指出这一点。有一些示例代码只是一个快速的技巧。实际的问题不是元数据和 for 循环的访问,而是 rs.next() 语句。此外,由于复杂的查询和 SQLite 的错误执行计划,这很慢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多