我在SQLite中有一個大表上的SELECT COUNT(*)出現性能問題。SQLite:COUNT在大桌子上慢
由於我還沒有收到可用的答案,我做了一些進一步的測試,我編輯了我的問題,以納入我的新發現。
我有2個表:
CREATE TABLE Table1 (
Key INTEGER NOT NULL,
... several other fields ...,
Status CHAR(1) NOT NULL,
Selection VARCHAR NULL,
CONSTRAINT PK_Table1 PRIMARY KEY (Key ASC))
CREATE Table2 (
Key INTEGER NOT NULL,
Key2 INTEGER NOT NULL,
... a few other fields ...,
CONSTRAINT PK_Table2 PRIMARY KEY (Key ASC, Key2 ASC))
表1有大約800萬條記錄和表2具有約51萬條記錄,而databasefile超過5GB。
表1有2個指標:
CREATE INDEX IDX_Table1_Status ON Table1 (Status ASC, Key ASC)
CREATE INDEX IDX_Table1_Selection ON Table1 (Selection ASC, Key ASC)
「狀態」是必填字段,但只有6個,「選擇」不要求不同的值,且只有大約150萬值從零,只有各地不同600k不同的值。
我在兩個表上做了一些測試,你可以看到下面的時間表,並且我爲每個請求(QP)添加了「解釋查詢計劃」。我將數據庫文件放置在USB存儲棒上,以便在每次測試後將其刪除,並獲得可靠的結果,而不受磁盤緩存的干擾。 USB上的一些請求速度更快(我想由於缺少seektime),但有些速度較慢(表掃描)。正如你所看到的,計數非常緩慢,但正常的選擇很快(除了第二個,這需要16秒)。
這同樣適用於表2:
SELECT COUNT(*) FROM Table2
Time: 528 sec
QP: SCAN TABLE Table2 USING COVERING INDEX sqlite_autoindex_Table2_1(~1000000 rows)
SELECT COUNT(Key) FROM Table2
Time: 249 sec
QP: SCAN TABLE Table2 (~1000000 rows)
SELECT * FROM Table2 WHERE Key = 5123456 AND Key2 = 0
Time: 7 ms
QP: SEARCH TABLE Table2 USING INDEX sqlite_autoindex_Table2_1 (Key=? AND Key2=?) (~1 rows)
爲什麼不使用自動創建的索引上表1的主鍵的SQLite? 爲什麼當他在Table2上使用自動索引時,它仍然需要很多時間?
我在SQL Server 2008 R2上創建了具有相同內容和索引的相同表,並且計數幾乎是瞬時的。
下面的一條評論建議在數據庫上執行ANALYZE。我做了,花了11分鐘才完成。 在那之後,我又跑了一些測試:
SELECT COUNT(*) FROM Table1
Time: 104 sec
QP: SCAN TABLE Table1 USING COVERING INDEX IDX_Table1_Selection(~7848023 rows)
SELECT COUNT(Key) FROM Table1
Time: 151 sec
QP: SCAN TABLE Table1 (~7848023 rows)
SELECT * FROM Table1 WHERE Status = 73 AND Key > 5123456 LIMIT 1
Time: 5 ms
QP: SEARCH TABLE Table1 USING INTEGER PRIMARY KEY (rowid>?) (~196200 rows)
SELECT COUNT(*) FROM Table2
Time: 529 sec
QP: SCAN TABLE Table2 USING COVERING INDEX sqlite_autoindex_Table2_1(~51152542 rows)
SELECT COUNT(Key) FROM Table2
Time: 249 sec
QP: SCAN TABLE Table2 (~51152542 rows)
正如你所看到的,查詢了同樣的時間(除了查詢計劃現在顯示行的實數),僅較慢選擇是現在也快。
接下來,我在Table1的Key字段上創建一個額外的索引,它應該對應於自動索引。我在原始數據庫上做了這個,沒有ANALYZE數據。創建此索引需要23分鐘以上(請記住,這是在USB棒上)。
CREATE INDEX IDX_Table1_Key ON Table1 (Key ASC)
然後我又跑測試:
SELECT COUNT(*) FROM Table1
Time: 4 sec
QP: SCAN TABLE Table1 USING COVERING INDEX IDX_Table1_Key(~1000000 rows)
SELECT COUNT(Key) FROM Table1
Time: 167 sec
QP: SCAN TABLE Table2 (~1000000 rows)
SELECT * FROM Table1 WHERE Status = 73 AND Key > 5123456 LIMIT 1
Time: 17 sec
QP: SEARCH TABLE Table1 USING INDEX IDX_Table1_Status (Status=?) (~3 rows)
正如你可以看到,指數幫助與COUNT(*),但與數(密鑰)。
CREATE TABLE Table1 (
Key INTEGER PRIMARY KEY ASC NOT NULL,
... several other fields ...,
Status CHAR(1) NOT NULL,
Selection VARCHAR NULL)
然後我又跑測試:
SELECT COUNT(*) FROM Table1
Time: 6 sec
QP: SCAN TABLE Table1 USING COVERING INDEX IDX_Table1_Selection(~1000000 rows)
SELECT COUNT(Key) FROM Table1
Time: 28 sec
QP: SCAN TABLE Table1 (~1000000 rows)
SELECT * FROM Table1 WHERE Status = 73 AND Key > 5123456 LIMIT 1
Time: 10 sec
QP: SEARCH TABLE Table1 USING INDEX IDX_Table1_Status (Status=?) (~3 rows)
儘管查詢計劃相同,則
Finaly,我使用列約束而不是表約束創建的表時間好得多。爲什麼是這樣 ?
問題是,ALTER TABLE不允許轉換現有的表,我有很多現有的數據庫,我不能轉換爲這種形式。另外,使用列約束而不是表約束對Table2不起作用。
有沒有人知道我做錯了什麼,以及如何解決這個問題?
我使用System.Data.SQLite版本1.0.74.0創建表並運行我使用SQLiteSpy 1.9.1的測試。
感謝,
馬克
如果你有SQLite的性能問題,該解決方案通常是移動到一個更大的數據庫服務器(我推薦使用MS SQL的Postgres)。 – Borealid
我沒有任何其他性能問題,所有其他選擇都很快(並使用正確的索引),插入和更新速度很快,但這只是讓我困擾的計數。 – Marc
這真的很奇怪,因爲(對於DB2來說,至少)大多數RDBMS可能使用有效緩存的信息 - 如果你要求_all_行的數量(或者受到索引中某些東西的限制),它通常可以讀取那些信息索引本身 - 索引知道條目的數量。這是非常奇怪的,因爲你說所有其他的SELECT都很快 - 他們需要知道記錄數量才能夠正確優化!除非有什麼奇怪的事情發生,並且你正在鎖定表(可重複讀取事務級別,或者其他一些?)... –