SQLite3 c++对5个双字段的SELECT查询的性能问题

SQLite3 c++ perfomance issues with SELECT query on 5 double fields

本文关键字:SELECT 查询 性能 问题 字段 c++ 5个 SQLite3      更新时间:2023-10-16

我使用SQLite3来存储约1 000 000个节点的5D规则网格,并且在"SELECT"查询的性能方面存在一些问题。

<<h2>上下文/h2>

数据库描述

每个条目由5+25个double组成,代表规则网格(节点)上的一个点:

  • 5第一个double:点在5D规则网格上的坐标(v1,v2,…,v5)
  • 25以下双精度:一些特性(p1,p2,…,p25)

每个点都是唯一的(前5个值的任何组合都是唯一的)。该表是用CREATE TABLE myTable(v1 double,..., v5 double, p1 double,..., p25 double)创建的。我没有添加特别的约束。

条目在它们的坐标之后按升序排序(v1,然后v2,然后v3,…):

v1|v2|v3|v4|v5|p1|p2|p3|...
 0| 0| 0| 0| 0| x| x| x|...
 0| 0| 0| 0| 1| x| x| x|...
 0| 0| 0| 0| 2| x| x| x|...
...
 0| 0| 0| 1| 0| x| x| x|...
 0| 0| 0| 1| 1| x| x| x|...
 0| 0| 0| 1| 2| x| x| x|...
...
我已经在这个表上创建了一个INDEX,使用CREATE INDEX idx ON myTable (v1,v2,v3,v4,v5)

SELECT查询描述

我想在5D网格中做一个"立方"插值。所以我需要在每个维度上,在我想要的点周围抽取4个点。我的SELECT查询应该返回4*4*4*4=1024点。

由于对称属性,我必须进行16次查询而不是1次。每个请求的格式为SELECT * FROM myTable WHERE (v1=X AND v2=X AND v3 BETWEEN x1 AND x2 AND v4 BETWEEN y1 AND y2 AND v5 BETWEEN z1 AND z2)。的确,v1和v2是角。在我的5D网格中,我的值从-165到180,步长为15°。因此,如果我想在-160插入一个值,我不能对v1 BETWEEN -180 AND -135类型进行查询(以获得-180,-165,-150,-135),因为-180在我的表中不存在。我可以优化这部分,只做4个查询,但这不是我主要关心的。

我使用一个准备好的语句: sqlite3_prepare_v2(db,"SELECT * FROM myTable WHERE (v1=? AND v2=? AND v3 BETWEEN ? AND ? AND v4 BETWEEN ? AND ? AND v5 BETWEEN ? AND ?"),length,statement,NULL)

然后,对于每个查询,我做:

sqlite3_bind_double(statement, int, double);
while(sqlite3_step(statement)==SQLITE_ROW) {
    // for each row (for each "node") :
    //   1) retrieve its properties
    double myvar1 = sqlite3_colum_double(statement,6)
    double myvar2 = sqlite3_colum_double(statement,7)
    ....
    double myvar25 = sqlite3_colum_double(statement,25)
    //   2) create the object and add it to a vector for the interpolation 
}
sqlite3_reset(statement);
sqlite3_clear_bindings(statement);

数据库以sqlite3_open_v2(path,&db,SQLITE_OPEN_READONLY,NULL)

执行16次查询并提取1024个点大约需要2.5秒。Sqlite3操作占用99.8%的计算量(callgrind)…

我做错了什么?

我试过EXPLAIN QUERY PLAN SELECT * FROM myTable WHERE (v1=0 AND v2=0 AND v3 BETWEEN 0 AND 3 AND v4 BETWEEN 0 AND 3 AND v5 BETWEEN 0 AND 3。结果是SEARCH TABLE myTable USING INDEX idx (v1=? AND v2=? AND v3>? AND v3<?)

基于5个坐标创建唯一索引并进行1024次查询会更快吗?在此之前,我使用vector和通过索引直接访问

普通索引在使用相等比较(=)进行查找时效果最好。正如您在EXPLAIN QUERY PLAN输出中看到的,非相等比较会阻止使用索引的任何其他列;数据库必须扫描所有可能的v4v5行来查找结果。

  1. 您在规则网格中查询相当少的点,因此您确切地知道您想要的所有点的坐标。只需使用一个简单的查询,用所有五个精确坐标搜索一个点,并执行1024次。这将导致更高效的单个索引查找,即使它是针对每个点执行的。

  2. 要使多个查询更高效,请将所有查询封装在单个事务中。
  3. 使用单独的R-tree索引查找点。r树针对(多维)间隔查询进行了优化。这将导致如下查询:

    SELECT *
    FROM myTable
    WHERE rowid IN (SELECT id
                    FROM RtreeIndexTable
                    WHERE v1 = ?
                      AND v2 = ?
                      AND v3 BETWEEN ? AND ?
                      AND v4 BETWEEN ? AND ?
                      AND v5 BETWEEN ? AND ?)
    

    r树通常用于不规则或稀疏数据;

  4. 如果您可以查询单个点,那么r树可能是多余的。