讓我直說吧。
你有一個表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
是一個緩慢的步驟,因爲數據庫不知道您想要哪個記錄,因此必須爲每個帖子和一條元數據(類型,語言或標記)組合創建一個臨時結果集。
如果這確實是您的問題,那麼正確的計劃看起來像這樣。加入pt1
至pta1
,在其上放置索引。加入pt2
至pta2
,然後加入到第一個查詢的結果中,然後加入到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;
除了隨機錯字等,這可能會表現得更好。如果沒有,請仔細檢查表格上的索引。
你做了一個[解釋](http://www.postgresql.org/docs/9.0/static/sql-explain.html)來查看查詢計劃嗎? – 2011-04-11 19:48:10
@Sam是的,我做了,我猜(根據我在許多地方發現的信息),這是Postgre本身的問題。現在我試圖「選擇COUNT(*)FROM帖子」,其中〜1 500 000條記錄和COUNTing記錄約9秒。第二次(Postgre可能會緩存查詢,或者其他什麼)花費約2秒。無論如何,我認爲2秒甚至是非常慢。 – 2011-04-12 09:21:57
您是否嘗試過創建索引?不要忘記部分索引。如果您只需要索引數據的一部分,它們可以產生巨大的性能差異。如果您已經完成了這些顯而易見的事情,但仍然無法改善,我建議您發佈更多詳細信息以獲得更多幫助(表格架構,示例數據,示例查詢,解釋計劃,postgresql版本等)。 – 2011-04-12 17:42:05