【问题标题】:Does SQLite3 supports indexing? How can I speed up a query?SQLite3 是否支持索引?如何加快查询速度?
【发布时间】:2013-09-04 07:14:33
【问题描述】:

我正在执行这个查询:

NSString *querySQL = [NSString stringWithFormat:@"
        SELECT DISTINCT P1.ID_RUTA_PARADAS
        FROM FastParadas AS P1
        WHERE P1.ID_ESTACION_INIT <= %d AND
            %d <= P1.ID_ESTACION_END
        INTERSECT
        SELECT DISTINCT P2.ID_RUTA_PARADAS
        FROM FastParadas AS P2
        WHERE P2.ID_ESTACION_INIT <= %d AND
            %d <= P2.ID_ESTACION_END",
    (int)estacionOrigen.ID_Estacion,(int)estacionOrigen.ID_Estacion,
    (int)estacionDestino.ID_Estacion,(int)estacionDestino.ID_Estacion];

我想加快速度。我尝试创建一些索引,但没有任何改进。 SQLite3 是否支持索引?

数据库有 3900+ 行,这个查询必须在不到一秒的时间内重复 1800+ 次。

【问题讨论】:

  • “祝你好运。”换句话说,现在放弃。让我们考虑一下这可能花费的最少时间:N = 4K 行。每秒 1.8K 次 * (N + N + N + N + N = 1.8K * 20K = 3600 万次“操作”/秒. 上帝禁止你敲磁盘。或者解析查询。或者做任何事情。你必须每秒评估这样的事情 1,800 次的唯一希望是将整个表加载到内存中并使用 C 代码而不是 SQL 来执行此操作。即便如此......
  • 感谢您的回答,这意味着没有内置 b-tree(用于索引),在 sqlite3 的次线性 log(n) 时间内执行查询?
  • 这里的问题不是 SQLite 实现的算法复杂性,而是每秒 1,800 次查询的要求。让我这样说:如果每个查询都导致一个 I/O 操作,并且您假设磁盘访问至少花费 0.5ms(然后等待返回到运行队列顶部的延迟)在执行 I/O 操作后关闭),您已经完全在水中死去。如果您打算将其全部存储在内存中,那么为什么要使用 SQLite 呢?只需使用结构数组和“查询”通过编写 C 代码来完成这项工作。 SQLite 在这里不是正确的工具。
  • 在这里,让我更直言不讳:在用户空间中做任何可能可能导致 I/O 达到 1.8KHz 的事情似乎是毫无希望的。据我估计,即使只有 5% 的操作导致 I/O,你也已经失败了(IIRC OS X 时间片是 10 毫秒。)
  • 将表加载到 RAM 中并使用纯 C 语言是一个不错的选择。然后通过利用对数据的某些限制,所花费的时间大大减少了。我接近1.8K/s

标签: sql objective-c sqlite


【解决方案1】:

数据库有3900+行,这个查询要重复1800+ 不到一秒的时间。

没有。在使用高度优化的算法扫描内存中数据的机器之外不会发生这种情况。

在任何这样的情况下,设计数据模型以使这种查询根本没有必要是至关重要的。 3900 多行确实不算多,但是针对该数据的 1800 多条查询是非常多的。

最好的办法是采用一种模式,消除每秒 1800 多个查询的需求,或者在最坏的情况下,设计应用程序,使每秒 1800 多个查询在进度条或其他东西后面完成。

【讨论】:

    【解决方案2】:

    除了@bbum 和@ipmcc 关于物理限制的观点之外,理论上您也不会对索引有太多运气。您正在寻找的是满足ID_ESTACION_INIT 小于某个值且ID_ESTACION_END 大于某个值的所有元组的ID_RUTA_PARADAS 条目(只是将其放入自然语言中)。

    索引对此有何帮助?

    (1) 假设您在ID_ESTACION_INIT 上有一个支持范围查询的索引。您可以相对较快地获得满足ID_ESTACION_INIT &lt;= %d 的行的所有ID。但是您必须获取所有这些行才能确定它们是否也满足%d &lt;= P1.ID_ESTACION_END

    (2) 假设您在ID_ESTACION_INIT 上有一个索引,在ID_ESTACION_END 上有一个索引,两者都支持范围查询。然后这两个都可以获得满足谓词的所有行,并且两个索引返回的rowid可以用于获取ID_RUTA_PARADA

    这两种方法的问题在于,如果您想使用它们,您将不得不对磁盘进行随机访问,这仅对小型结果集有意义(即,如果满足这些谓词的行数很少) . 对于更大的基数(我想我听说过 >= 5%,但这也可能只是一个示例),您的数据库系统将进行表扫描以查找所有元组,这意味着您的索引没有帮助。

    这里有一个 SQLFiddle 来玩弄索引,也许还有其他 DBMS:http://sqlfiddle.com/#!5/d1a86/2

    (事实上,聚集索引可以帮助读取更少的非限定元组,但 SQLite 不支持它们:sqlite: Fastest way to get all rows (consecutive disk access)

    【讨论】:

      【解决方案3】:

      在此查询中,INTERSECT 已经负责删除重复项,因此您不需要 DISTINCT。以下查询可能会更快:

      SELECT DISTINCT ID_RUTA_PARADAS
      FROM FastParadas
      WHERE %d BETWEEN ID_ESTACION_INIT AND ID_ESTACION_END
        AND %d BETWEEN ID_ESTACION_INIT AND ID_ESTACION_END
      

      但是,像这样的范围查询无法使用普通索引轻松优化。 您应该将数据库更改为使用一维 R-tree index,在这种情况下,每秒可能有 1800 次查询。

      【讨论】:

      • 不,%ds 被两个SELECTs 的不同字符串替换;一次是estacionOrigen,另一次是estacionDestino
      • @contradictioned 这就是为什么还有两个%ds。
      • 我执行了这个查询而不是上面的那个。这是一个类似但本质上不同的查询,并给出了在广泛测试下不正确的其他答案。
      猜你喜欢
      • 1970-01-01
      • 2016-10-30
      • 1970-01-01
      • 2014-10-04
      • 1970-01-01
      • 2012-05-24
      • 2012-10-26
      • 2015-04-07
      相关资源
      最近更新 更多