2011-01-13 30 views
11

我有一張大約2000萬行的表格。出於理由,可以說表中有兩列 - 一個id和一個時間戳。我試圖計算每天的項目數量。這是我目前所擁有的。在postgres的大桌子上加速按日期查詢

SELECT DATE(timestamp) AS day, COUNT(*) 
    FROM actions 
    WHERE DATE(timestamp) >= '20100101' 
    AND DATE(timestamp) < '20110101' 
GROUP BY day; 

沒有任何索引,這需要大約30秒才能在我的機器上運行。這裏的解釋分析輸出:

GroupAggregate (cost=675462.78..676813.42 rows=46532 width=8) (actual time=24467.404..32417.643 rows=346 loops=1) 
    -> Sort (cost=675462.78..675680.34 rows=87021 width=8) (actual time=24466.730..29071.438 rows=17321121 loops=1) 
     Sort Key: (date("timestamp")) 
     Sort Method: external merge Disk: 372496kB 
     -> Seq Scan on actions (cost=0.00..667133.11 rows=87021 width=8) (actual time=1.981..12368.186 rows=17321121 loops=1) 
       Filter: ((date("timestamp") >= '2010-01-01'::date) AND (date("timestamp") < '2011-01-01'::date)) 
Total runtime: 32447.762 ms 

因爲我看到一個順序掃描,我試圖指數上約50%的切割速度的日期總

CREATE INDEX ON actions (DATE(timestamp)); 

HashAggregate (cost=796710.64..796716.19 rows=370 width=8) (actual time=17038.503..17038.590 rows=346 loops=1) 
    -> Seq Scan on actions (cost=0.00..710202.27 rows=17301674 width=8) (actual time=1.745..12080.877 rows=17321121 loops=1) 
     Filter: ((date("timestamp") >= '2010-01-01'::date) AND (date("timestamp") < '2011-01-01'::date)) 
Total runtime: 17038.663 ms 

我是新來的整個查詢優化業務,我不知道下一步該怎麼做。任何線索如何讓這個查詢運行得更快?

- 編輯 -

它看起來像我打指標的限制。這幾乎是在該表上運行的唯一查詢(儘管日期的值會更改)。有沒有辦法分割桌子?或者用所有計數值創建一個緩存表?還是有其他選擇?

+2

您是否在此告訴我們整個故事?它看起來像改變了第一個和第二個計劃之間的內存設置。這是我的建議。 ;-) – 2011-01-13 07:29:13

+0

內存設置沒有變化,雖然我一直在每個模式更改之間進行真空分析。不知道我是否應該這樣做,但它確實會嚴重影響結果。 – zaius 2011-01-13 08:00:20

回答

5

有沒有辦法分割表格?

是:
http://www.postgresql.org/docs/current/static/ddl-partitioning.html

或者創建擁有所有的計數值緩存表?還是有其他選擇?

當然可以創建一個「緩存」表。但這取決於你需要多久結果以及它需要多準確。

 
CREATE TABLE action_report 
AS 
SELECT DATE(timestamp) AS day, COUNT(*) 
    FROM actions 
    WHERE DATE(timestamp) >= '20100101' 
    AND DATE(timestamp) < '20110101' 
GROUP BY day; 

然後,SELECT * FROM action_report會給你及時想要什麼。然後您將安排一個cron作業定期重新創建該表。

如果每個查詢的時間範圍發生變化,或者該查詢每天只運行一次,那麼這種方法當然無濟於事。

1

它看起來像範圍涵蓋了所有可用的數據。

這可能是一個設計問題。如果您經常運行此操作,則最好創建一個僅包含日期的額外列timestamp_date。然後在該列上創建索引,並相應地更改查詢。該列應該通過插入+更新觸發器來維護。

SELECT timestamp_date AS day, COUNT(*) 
FROM actions 
WHERE timestamp_date >= '20100101' 
    AND timestamp_date < '20110101' 
GROUP BY day; 

如果我錯了行數的日期範圍會找到(它只是一小部分),那麼你可以嘗試的索引上只是時間戳列本身,應用WHERE子句只列(其中給出的範圍內的作品一樣好)

SELECT DATE(timestamp) AS day, COUNT(*) 
FROM actions 
WHERE timestamp >= '20100101' 
    AND timestamp < '20110101' 
GROUP BY day; 
+0

是的,該過濾器適用於數據的大部分子集。查詢更小的子集通常足夠快 - 這只是大的查詢速度慢。我試着添加timestamp_date列和一個索引。不幸的是,這兩個選項都沒有顯着加速。這是輸出:http://pastie.org/1454799 – zaius 2011-01-13 01:35:57

+2

爲什麼這會有所作爲?帶有'date(timestamp)'的查詢的'date(timestamp)'索引與`timestamp_date`的索引具有相同的效果,查詢涉及`timestamp_date`列。 – 2011-01-13 07:28:01

0

嘗試運行explain analyze verbose ...,看看總使用臨時文件。也許增加work_mem以允許在內存中完成更多操作?

2

一般來說,如果預期的返回行數很高,大多數數據庫將忽略索引。這是因爲對於每個索引命中,它還需要查找該行,因此只需執行全表掃描就會更快。這個數字在10,000到100,000之間。您可以通過縮小日期範圍並查看postgres翻轉爲使用索引的位置來嘗試此操作。在這種情況下,postgres計劃掃描17,301,674行,所以你的表格非常大。如果你真的很小,你仍然覺得postgres做出了錯誤的選擇,然後嘗試在表上運行一個分析,讓postgres得到它的近似值。

-1

設置work_mem說2GB,看看是否改變計劃。如果沒有,你可能沒有選擇。

0

你真正想要的這種DSS類型查詢是描述天的日期表。在數據庫設計術語中,它被稱爲日期維度。要填充這樣的表格,您可以使用我在本文中發佈的代碼:http://www.mockbites.com/articles/tech/data_mart_temporal

然後在動作表中的每一行中放置相應的date_key。

你的查詢就變成了:

SELECT 
    d.full_date, COUNT(*) 
FROM actions a 
JOIN date_dimension d 
    ON a.date_key = d.date_key 
WHERE d.full_date = '2010/01/01' 
GROUP BY d.full_date 

假設上的按鍵和FULL_DATE指數,這將是超級快,因爲它在INT4鍵進行操作!

另一個好處是,您現在可以通過任何其他date_dimension列進行切片和切塊。