所以我试图将SQLite与一个相当基本的SQL查询一起使用(对于那些不熟悉GLOB的人,它类似于LIKE):
SELECT * FROM dictionary where word GLOB '[paple][paple][paple][paple][paple]';
我可以在SQLite Manager中运行它,它需要大约50ms来检索所有记录。现在我用Java编写以下代码,它需要将近1.5秒,相比之下看起来非常慢。我知道它可能需要更长的时间,但是长1450毫秒是不可接受的慢:
Connection conn = DriverManager.getConnection("jdbc:sqlite:dictionary.sqlite");
Statement stat = conn.createStatement();
long start = System.currentTimeMillis();
ResultSet rs = stat.executeQuery("SELECT * FROM dictionary where word GLOB '[paple][paple][paple][paple][paple]';");
while (rs.next()) {
System.out.println("word = " + rs.getString("word"));
}
rs.close();
conn.close();
long end = System.currentTimeMillis();
System.out.println("Took: " + (end - start));
我有一种感觉,每当我调用ResultSet.next()时,它都必须重新查询数据库,因为它不会立即获得所有记录,但我不是100%肯定。我觉得应该有一个更有效的方法来做到这一点。所以我的问题是,是否有人知道如何更快地改进Java代码?
PS:我正在使用sqliteJDBC。这里的实施能否减缓我的速度?只是我的想法。
答案 0 :(得分:1)
每当你致电ResultSet#getString(String)
时,你都要做很多工作。请参阅the JDBC driver的its internal method RS#findColumn(String)
代码。请注意,它不会缓存column-name-to-column-ordinal-index映射。对于您检查的结果集中的每一行,您将遭受多个字符串比较和大小写转换操作。
尝试将ResultSet#getString(String)
替换为ResultSet#getString(int)
。首先,在while
循环之外的早期,找出要提取的列的索引。 (请注意,用显式列列表替换SELECT *
会好得多,在这种情况下,您已经知道每列的序数索引。)
final int indexWord = rs.findColumn("word");
然后,在迭代期间,使用先前确定的索引:
// Avoid concatenating:
System.out.print("word = ");
System.out.println(rs.getString(indexWord));
让我们知道优化是否会产生明显的影响。
答案 1 :(得分:0)
Java代码对我来说很好看。主要问题是它将进行线性表扫描,在大型数据库上可能相当慢,而word
列上的索引将无济于事(或者至少不会有太大帮助) )。
您正在使用的SQLite的基础版本是什么?使用当前版本可能会启用更多优化。 (我问,因为sqliteJDBC已经存在了几年,但SQLite已嵌入到驱动程序中 - 当然,因为它是一个嵌入式数据库而不是数据库服务器 - 从那时起就有不少版本。)
答案 2 :(得分:0)
我使用小型数据库遇到了同样的问题。我的代码与此类似:
public LinkedList<Person> getByType(Type type) {
LinkedList<Person> list = new LinkedList<>();
String query = "SELECT * FROM person WHERE type_id = " + String.valueOf(type.getId());
try {
ResultSet rs = executeQuery(query); // Just calls statement.executeQuery(query);
logTimestamp("After executeQuery");
while (rs.next()) {
logTimestamp("After rs.next");
Person person = buildPersonFromResultSet(rs); // Just instances a new Person(rs.getLong("id"), rs.getString("name"));
logTimestamp("After buildPersonFromResultSet");
list.add(person);
logTimestamp("After list.add");
// Each loop iteration takes less than 1 ms
}
// list.size() is 26
logTimestamp("After the last rs.next"); // After the last rs.next(), it was taking 4 seconds!
} catch (Exception e) {
LOGGER.error("Could not list. Query=[" + query + "]", e);
}
return list;
}
通过带时间戳的日志,我注意到仅在最后一次调用rs.next()
方法时发生了4秒的减速。我看了一下SQLite JDBC驱动程序源代码(https://bitbucket.org/xerial/sqlite-jdbc/src),看到当“fetch”游标发现他在最后一行时发生了很多事情。我试图增加语句的获取大小(正如其他答案所指出的那样),但没有成功。我听说应该将数据库表编入索引以简化该工作。当我检查我的表时,我很惊讶,因为主键和外键中没有索引。默认情况下,某些数据库工具不会创建索引,所以我这样做了,现在最后一次迭代也不到1毫秒。
所以,总结一下:
我的SQLite数据库没有索引。在为主键和外键创建它们之后,所有循环都需要20毫秒而不是4秒。
答案 3 :(得分:0)
相当老:)但我们遇到了完全相同的问题:一个返回~1500结果的查询,在SQLite CLI中执行50-100ms,使用JDBC驱动程序在40&000; 000 ms内执行。
99%的时间花在rs.next
上我们将sqlite-jdbc库从3.7升级到最新版本(3.8.11),性能大致乘以1000。