大表中的慢速索引掃描
2020 年 8 月 4 日更新:
由於顯然仍在定期查看此答案,因此我想提供有關情況的更新。我們目前正在使用啟用表分區的 PG 11,
timestamp
並且可以輕鬆處理表中的數十億行。僅索引掃描是救命稻草,沒有它就不可能。使用 PostgreSQL 9.2,我在相對較大的表(200+ 百萬行)上遇到慢查詢問題。我沒有嘗試任何瘋狂的事情,只是添加了歷史價值。下面是查詢和查詢計劃輸出。
我的表格佈局:
Table "public.energy_energyentry" Column | Type | Modifiers -----------+--------------------------+----------------------------------------------------------------- id | integer | not null default nextval('energy_energyentry_id_seq'::regclass) prop_id | integer | not null timestamp | timestamp with time zone | not null value | double precision | not null Indexes: "energy_energyentry_pkey" PRIMARY KEY, btree (id) "energy_energyentry_prop_id" btree (prop_id) "energy_energyentry_prop_id_timestamp_idx" btree (prop_id, "timestamp") Foreign-key constraints: "energy_energyentry_prop_id_fkey" FOREIGN KEY (prop_id) REFERENCES gateway_peripheralproperty(id) DEFERRABLE INITIALLY DEFERRED
數據范圍從2012-01-01到現在,不斷增加新數據。外鍵中有大約 2.2k 個不同的值
prop_id
,分佈均勻。我注意到行估算並不遙遠,但成本估算似乎大了 4 倍。這可能不是問題,但我能做些什麼嗎?
我預計磁碟訪問可能是問題,因為該表並非一直在記憶體中。
EXPLAIN ANALYZE SELECT SUM("value") FROM "energy_energyentry" WHERE "prop_id"=82411 AND "timestamp">'2014-06-11' AND "timestamp"<'2014-11-11' ;
Aggregate (cost=214481.45..214481.46 rows=1 width=8) (actual time=51504.814..51504.814 rows=1 loops=1) -> Index Scan using energy_energyentry_prop_id_timestamp_idx on energy_energyentry (cost=0.00..214434.08 rows=18947 width=8) (actual time=136.030..51488.321 rows=13578 loops=1) Index Cond: ((prop_id = 82411) AND ("timestamp" > '2014-06-11 00:00:00+00'::timestamp with time zone) AND ("timestamp" < '2014-11-11 00:00:00+00'::timestamp with time zone)) Total runtime: 51504.841 ms
有什麼建議可以加快速度嗎?
我也很高興聽到我沒有做任何奇怪的事情。
您的表很大,跨整個表的任何索引也是如此。假如說:
- 僅輸入新數據(帶
timestamp = now()
)- 現有行既不會更改也不會刪除。
- 您有自 2012-01-01 以來的數據,但查詢主要針對當年(?)
我建議使用部分、多列(覆蓋!)索引:
CREATE INDEX ON energy_energyentry (prop_id, "timestamp", value) WHERE "timestamp" >= '2014-01-01 0:0'; -- adapt to your needs
在 Postgres 11 或更高版本中,請改用“覆蓋”索引:
CREATE INDEX ON energy_energyentry (prop_id, "timestamp") INCLUDE (value) WHERE "timestamp" >= '2014-01-01 0:0';
看:
只包括定期查詢的時間範圍。僅當您以這種方式切斷大部分大桌子時才有意義。隨著新條目的出現,效果會隨著時間的推移而惡化。不時重新創建索引。(您可能需要調整您的查詢。)請參閱下面的連結答案。
僅包含最後一列值以從中獲取僅索引掃描。積極的 autovacuum 設置可能有助於保持可見性地圖是最新的,就像 @jjanes 已經提到的那樣。
部分索引應該更容易放入 RAM 並保持更長的時間。
您可能需要
WHERE
在查詢中包含此條件,以使計劃者了解索引適用於查詢,例如:SELECT sum(value) AS sum_value FROM energy_energyentry WHERE prop_id = 82411 AND "timestamp" > '2014-06-11 0:0' AND "timestamp" < '2014-11-11 0:0' AND "timestamp" >= '2014-01-01 0:0'; -- seems redundant, but may be needed
由於您的查詢匯總了很多行 (
rows=13578
),因此即使使用僅索引掃描,這也需要一些時間。不過,它不應該接近 50 秒。在任何中等體面的硬體上不到一秒鐘。相關(但忽略
CLUSTER
andFILLFACTOR
,如果您可以從中獲得僅索引掃描,則兩者都無關緊要):旁白:
由於您目前在 上有一個索引
(prop_id, "timestamp")
,因此在 just 上的附加索引(prop_id)
可能會花費超過其價值: