2012-10-18 123 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特定的解決方案來在低基數列上進行查詢/索引。