【问题标题】:SqlLite in memory database slows ResultSet next内存数据库中的 Sqlite 下一个会减慢 ResultSet
【发布时间】:2013-12-06 10:24:46
【问题描述】:

我正在使用纬度和经度作为索引,并将 r*tree 模块编译到 sqlite3 数据库中以提高性能。此外,我将表加载到内存数据库中。令人惊讶的结果是,如果数据来自硬盘,ResultSet next 方法会减慢到 25-30 毫秒而不是 1 毫秒。通常我希望结果集有 250 个条目。

首先建立到内存的连接。

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

然后将表从硬盘复制到内存中:

Statement s = connection.createStatement(); 
s.execute("ATTACH 'myDB.db' AS fs");
s.executeUpdate("CREATE VIRTUAL TABLE coordinates USING rtree(id, min_longitude, max_longitude, min_latitude, max_latitude)");
s.executeUpdate("INSERT INTO coordinates (id, min_longitude, max_longitude, min_latitude, max_latitude)
SELECT id, min_longitude, max_longitude, min_latitude, max_latitude FROM fs.coordinates");
s.executeUpdate("CREATE TABLE locations AS SELECT * from fs.locations");
s.execute("DETACH DATABASE fs");

最后一步是查询数据库并将结果复制到一个对象中。

final String sql = "SELECT * FROM locations, coordinates WHERE (locations.id = coordinates.id) AND ((min_latitude >= ? AND max_latitude <= ?) AND (min_longitude >= ? AND max_longitude <= ?))";
PreparedStatement ps = connection.prepareStatement(sql);
// calculate bounding rec and fullfil sql statement. 
Result rs = ps.executeQuery();
while (rs.next()) {
   // copy the stuff here
}

我尝试了一些东西,例如连接应该是“只读”或使用 TYPE_FORWARD_ONLY 和 CONCUR_READ_ONLY 或增加获取大小来执行结果集,但 rs.next() 保持不变。

有没有人知道内存中发生了什么以及为什么rs.next() 这么慢?如何增加查询?

系统使用来自org.xerial和tomcat6的驱动sqlite-jdbc 3.7.2。

更新:添加了数据库架构

CREATE VIRTUAL TABLE coordinates USING rtree(
 id,
 min_latitude,
 max_latitude,
 min_longitude,
 max_longitude);

CREATE TABLE locations(
 id INTEGER UNIQUE NOT NULL,
 number_of_locations INTEGER DEFAULT 1,
 city_en TEXT DEFAULT NULL,
 zip TEXT DEFAULT NULL,
 street TEXT DEFAULT NULL,
 number TEXT DEFAULT NULL,
 loc_email TEXT DEFAULT NULL,
 loc_phone TEXT DEFAULT NULL,
 loc_fax TEXT DEFAULT NULL,
 loc_url TEXT DEFAULT NULL);

【问题讨论】:

  • 显示原始数据库的数据库架构,包括约束和索引。
  • 我想知道您关于索引的问题。我的理解是 (min_latitude, max_latitude, min_longitude, max_longitude) 是一个新的二维索引,应该可以提高速度。 id 是两个表之间的链接。

标签: java database sqlite database-performance


【解决方案1】:

在原始数据库中,查询首先使用 R-tree 索引查找坐标,然后在 id 列上查找与索引(由 UNIQUE 暗示)匹配的位置,如 EXPLAIN QUERY PLAN 输出所示:

0|0|1|SCAN TABLE coordinates VIRTUAL TABLE INDEX 2:DaBbDcBd
0|1|0|SEARCH TABLE locations USING INDEX sqlite_autoindex_locations_1 (id=?)

在内存数据库中,表架构发生了变化because

使用 CREATE TABLE AS 创建的表没有 PRIMARY KEY,也没有任何类型的约束。

这个特定的表现在如下所示:

CREATE TABLE locations(
  id INT,
  number_of_locations INT,
  city_en TEXT,
  zip TEXT,
  street TEXT,
  number TEXT,
  loc_email TEXT,
  loc_phone TEXT,
  loc_fax TEXT,
  loc_url TEXT
);

不再可能通过 ID 有效地查找位置,因此数据库会进行全表扫描:

0|0|0|SCAN TABLE locations
0|1|1|SCAN TABLE coordinates VIRTUAL TABLE INDEX 1:

复制数据库时,应始终使用所有原始 CREATE TABLE/INDEX 语句。

【讨论】:

  • 看来您发现了问题。也许您想知道为什么我以两种不同的方式复制表格。当我尝试像第二个位置一样复制虚拟表坐标时,我遇到了 SQL 格式异常。我在想这与三个隐藏表“坐标_父坐标_节点坐标_rowid”有关。使用 CREATE 和 INSERT 复制表位置(如 coodinates)会再次导致此全表扫描,这是不想要的副作用^^。如何有效地复制表格或正确重新创建?
  • 我不明白;使用 CREATE/INSERT 复制 locations 有什么问题?
  • 我按照您的描述或我对表格位置的理解更改了我的代码。 statement.executeUpdate("创建表位置(见上文)"); statement.executeUpdate("插入位置选择 * FROM fs.locations");但运行时行为没有变化。从我的角度来看,可能没有创建虚拟表或链接坐标.id == locatians.id 不起作用。
  • 我现在仔细查看了 rtree 模块的隐藏表。这三个都有一个整数作为主键。这可能是它在硬盘上工作而不在内存中工作的原因吗?是否有更改连接到内存数据库以进行调试?
  • 问题是locations.id 列上缺少索引。您的新 locations 表是否具有 UNIQUE 约束? EXPLAIN QUERY PLAN 输出是什么?
猜你喜欢
  • 2013-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多