2011-04-11 162 views
1

我有個職位,其中由分類表:PostgreSQL緩慢COUNT() - 是否觸發唯一的解決方案?

  • 標籤
  • 語言

所有這些 「類」 被存儲在接下來的表(posts_types)和通過下表連接(posts_types_assignment)。

COUNTing in PostgreSQL is really slow(我在該表中有超過500k條記錄),我需要獲取按類型/ tag/lang的任意組合分類的帖子數量。

如果我通過觸發器來解決它,它會充滿許多多級循環,這看起來確實看起來不錯,而且很難維護。

是否有任何其他解決方案如何有效地獲得任何類型/標籤/語言分類的帖子的實際數量?

+0

你做了一個[解釋](http://www.postgresql.org/docs/9.0/static/sql-explain.html)來查看查詢計劃嗎? – 2011-04-11 19:48:10

+0

@Sam是的,我做了,我猜(根據我在許多地方發現的信息),這是Postgre本身的問題。現在我試圖「選擇COUNT(*)FROM帖子」,其中〜1 500 000條記錄和COUNTing記錄約9秒。第二次(Postgre可能會緩存查詢,或者其他什麼)花費約2秒。無論如何,我認爲2秒甚至是非常慢。 – 2011-04-12 09:21:57

+1

您是否嘗試過創建索引?不要忘記部分索引。如果您只需要索引數據的一部分,它們可以產生巨大的性能差異。如果您已經完成了這些顯而易見的事情,但仍然無法改善,我建議您發佈更多詳細信息以獲得更多幫助(表格架構,示例數據,示例查詢,解釋計劃,postgresql版本等)。 – 2011-04-12 17:42:05

回答

1

讓我直說吧。

你有一個表posts。你有一個表posts_types。這兩人在posts_types_assignment上有多次加入。你有這樣的一些查詢是慢的:

SELECT count(*) 
FROM posts p 
    JOIN posts_types_assigment pta1 
    ON p.id = pta1.post_id 
    JOIN posts_types pt1 
    ON pt1.id = pta1.post_type_id 
     AND pt1.type = 'language' 
     AND pt1.name = 'English' 
    JOIN posts_types_assigment pta2 
    ON p.id = pta2.post_id 
    JOIN posts_types pt2 
    ON pt2.id = pta2.post_type_id 
     AND pt2.type = 'tag' 
     AND pt2.name = 'awesome' 

而你想知道爲什麼它是痛苦的緩慢。

我的第一個注意事項是,如果您在posts表中有標識符而不是在連接中,PostgreSQL將不得不做很少的工作。但這是一個沒有實際意義的問題,已經做出了決定。

我更有用的說明是我相信PostgreSQL有一個類似於Oracle的查詢優化器。在這種情況下,爲了限制必須考慮的可能查詢計劃的組合爆炸,它只考慮以某個表開始的計劃,然後一次重複連接另一個數據集。然而,沒有這樣的查詢計劃在這裏工作。你可以從pt1開始,得到1條記錄,然後去pta1,拿到一堆記錄,加入p,用相同數量的記錄結束,然後加入pta2,現在你得到了大量的記錄,然後加入pt2,只有幾個記錄。加入pta2是一個緩慢的步驟,因爲數據庫不知道您想要哪個記錄,因此必須爲每個帖子和一條元數據(類型,語言或標記)組合創建一個臨時結果集。

如果這確實是您的問題,那麼正確的計劃看起來像這樣。加入pt1pta1,在其上放置索引。加入pt2pta2,然後加入到第一個查詢的結果中,然後加入到p。然後算。這意味着我們不會得到巨大的結果集。

如果出現這種情況,沒有辦法告訴查詢優化器,一旦您想要它想出新的執行計劃類型。但是有辦法強制它。

CREATE TEMPORARY TABLE t1 
AS 
SELECT pta* 
FROM posts_types pt 
    JOIN posts_types_assignment pta 
    ON pt.id = pta.post_type_id 
WHERE pt.type = 'language' 
    AND pt.name = 'English'; 

CREATE INDEX idx1 ON t1 (post_id); 

CREATE TEMPORARY TABLE t2 
AS 
SELECT pta* 
FROM posts_types pt 
    JOIN posts_types_assignment pta 
    ON pt.id = pta.post_type_id 
    JOIN t1 
    ON t1.post_id = pta.post_id 
WHERE pt.type = 'language' 
    AND pt.name = 'English'; 

SELECT COUNT(*) 
FROM posts p 
    JOIN t1 
    ON p.id = t1.post_id; 

除了隨機錯字等,這可能會表現得更好。如果沒有,請仔細檢查表格上的索引。

1

由於btilly筆記,如果他猜中的模式,表的設計並不能幫助 - 它似乎(一見鍾情,至少),例如,有三個表posts_tag(post_id,tag)post_lang(post_id,lang)post_type(post_id,type)會更自然並且效率更高。

除此之外(或除此之外),可以考慮總結所有可能計數的表格或物化視圖,列號爲(lang,type,tag,nposts)。當然,要全面計算這個值會非常慢,但是(除了第一次)可以完全「在後臺」,在某個時間間隔內完成(如果數據變化不大,噸需要確切的計數),或者急切地使用觸發器。 舉例看看here

相關問題