2012-10-18 198 views
2

我有一个包含大约500K行的表。该表在“状态”列中有一个索引。于是我运行下面的命令解释:SQLite ANALYZE中断索引

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

结果在可预见的“说明” ......

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

后许多额外的行添加到表中,我称之为“分析”。随后,查询似乎要慢得多,所以我重新运行我的解释,现在看到以下内容:

SCAN TABLE my_table (~6033 rows) 

我注意到的第一件事是,无论是行估计的路要走。最大的问题是,一旦ANALYZE运行,该指数似乎会被跳过。我尝试了REINDEX - 无济于事。我能够得到索引的唯一方法是放弃它们,然后重新创建它们。有没有人看过这个?这是一个错误?任何想法我做错了什么?我已经在多个数据库上试过了,我看到了相同的结果。这是在我的电脑上,在MAC和iPhone/iPad上 - 都是一样的结果。

回答

1

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

但是,SQLite的查询规划器没有关于索引或表中记录大小的信息,所以它的估计可能是关闭的。

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

如果您在索引字段上使用ORDER BY,则可以强制检索索引。 (INDEXED BY是,它的文档中说,拟用于调整查询的性能。)

如果你不需要选择表中的所有领域,可以加快特定查询通过在这些查询的字段上创建一个索引,以便您有一个covering index

+0

我添加了order by子句,它确实强制使用索引。感谢这两个答案 - 都帮了很大忙。 – Brandon

0

查询执行计划避免在像“status”这样的低基数列上使用现有索引并不少见,该列可能只有一些不同的值。通过扫描数据库表来执行查找通常更快。 (一些DBA建议从来没有索引的低基数列。)

然而,基于解释计划的疯狂不同的行数,我猜测说的SQLite的“分析”也执行MySQL的“分析”使用时InnoDB存储引擎。 MySQL的“分析”会随机对表数据进行潜水以确定行数,索引基数等。由于随机潜水,每次“分析”运行后统计数据可能会有所不同,并导致不同的查询执行计划。低基数列更容易出现不正确的统计数据,例如,随机潜水可能表明表中的大部分行都处于“活动”状态,使表格扫描更有效率,而不是使用索引。 (我不是SQLite的专家,所以有人请帮腔,如果我对“分析”行为的直觉是不正确。)

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