SQLite ANALYZE打破了索引

时间:2012-10-18 04:39:57

标签: iphone ios sqlite

我有一个包含大约500K行的表。该表在“status”列上有一个索引。所以我运行以下解释命令:

EXPLAIN QUERY PLAN SELECT * FROM my_table WHERE status = 'ACTIVE'

产生可预测的“解释”......

SEARCH TABLE my_table USING INDEX IDX_my_table_status (status=?) (~10 rows)

在表中添加了许多其他行后,我调用'ANALYZE'。之后,查询似乎要慢得多,所以我重新运行了解释,现在看到以下内容:

SCAN TABLE my_table (~6033 rows)

我注意到的第一件事就是行估计是关闭的。最大的问题是,一旦运行ANALYZE,索引似乎就会被跳过。我试过REINDEX - 无济于事。我可以获取索引的唯一方法是删除它们,然后重新创建它们。有没有人见过这个?这是一个错误吗?我有什么想法我做错了吗?我在多个数据库上尝试了这个,我看到了相同的结果。这是在我的电脑上,在MAC和iPhone / iPad上 - 都是相同的结果。

2 个答案:

答案 0 :(得分:1)

当SQLite使用索引从表中获取行时,它必须首先读取索引页,然后读取包含一个或多个匹配记录的所有表的页面。 如果有许多匹配的记录,几乎所有表的页面都可能包含一个,因此浏览索引需要阅读更多页面。

但是,SQLite的查询计划程序没有关于索引或表中记录大小的信息,因此其估计值可能已关闭。

ANALYZE收集的信息存储在sqlite_stat1 and maybe sqlite_stat3 tables中。 请显示有关您的餐桌的信息 如果该信息不能反映数据的真实分布,您可以尝试再次运行ANALYZE,或者只是从sqlite_stat*表中删除该信息。

如果在索引字段上使用ORDER BY,则可以强制浏览索引。 (INDEXED BY正如其文档所述, not 旨在用于调优查询的性能。)

如果您不需要选择表格的所有字段,则可以通过在这些查询上创建索引来加快特定查询的速度。字段,以便您有covering index

答案 1 :(得分:0)

查询执行计划避免在像“status”这样的低基数列上使用现有索引并不罕见,这可能只有几个不同的值。通过扫描db表来执行查找通常会更快。 (有些DBA建议永远不要为低基数列编制索引。)

然而,基于解释计划中大量不同的行数,我猜测在使用InnoDB存储引擎时,SQLite的'analyze'与MySQL的'analyze'表现相似。 MySQL的“分析”对表格数据进行随机潜水,以确定行数,索引基数等。由于随机潜水,统计信息可能会在每次“分析”运行后发生变化,从而导致不同的查询执行计划。低基数列甚至更容易受到不正确的统计信息的影响,例如,随机潜水可能表示表中的大多数行具有“活动”状态,这使得表扫描更有效,而不是使用索引。 (我不是SQLite的专家,所以如果我对“分析”行为的预感不正确,请有人加入。)

您可以尝试使用“indexed by”(请参阅​​http://www.sqlite.org/lang_indexedby.html)在查询中测试索引的使用,尽管强制使用索引通常是最后的选择。不同的RDBMS对低基数问题有不同的解决方案,例如分区,使用位图索引等。我建议研究SQLite特定的解决方案来查询/索引低基数列。)