簡體   English   中英

使用ORDER和LIMIT子句進行極慢的PostgreSQL查詢

[英]Extremely slow PostgreSQL query with ORDER and LIMIT clauses

我有一張桌子,我們稱之為“foos”,其中有近600萬條記錄。 我正在運行以下查詢:

SELECT "foos".*
FROM "foos"
INNER JOIN "bars" ON "foos".bar_id = "bars".id
WHERE (("bars".baz_id = 13266))
ORDER BY "foos"."id" DESC
LIMIT 5 OFFSET 0;

此查詢需要很長時間才能運行(Rails在運行時會超時)。 所有ID都有一個索引。 好奇的部分是,如果我刪除ORDER BY子句或LIMIT子句,它幾乎立即運行。

我假設ORDER BYLIMIT存在使得PostgreSQL在查詢規划中做出了一些糟糕的選擇。 任何人都有任何想法如何解決這個問題?

如果它有幫助,這里是所有3種情況的EXPLAIN

//////// Both ORDER and LIMIT
SELECT "foos".*
FROM "foos"
INNER JOIN "bars" ON "foos".bar_id = "bars".id
WHERE (("bars".baz_id = 13266))
ORDER BY "foos"."id" DESC
LIMIT 5 OFFSET 0;
                                                     QUERY PLAN                                                     
--------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.00..16663.44 rows=5 width=663)
   ->  Nested Loop  (cost=0.00..25355084.05 rows=7608 width=663)
         Join Filter: (foos.bar_id = bars.id)
         ->  Index Scan Backward using foos_pkey on foos  (cost=0.00..11804133.33 rows=4963477 width=663)
               Filter: (((NOT privacy_protected) OR (user_id = 67962)) AND ((status)::text = 'DONE'::text))
         ->  Materialize  (cost=0.00..658.96 rows=182 width=4)
               ->  Index Scan using index_bars_on_baz_id on bars  (cost=0.00..658.05 rows=182 width=4)
                     Index Cond: (baz_id = 13266)
(8 rows)

//////// Just LIMIT
SELECT "foos".*
FROM "foos"
INNER JOIN "bars" ON "foos".bar_id = "bars".id
WHERE (("bars".baz_id = 13266))
LIMIT 5 OFFSET 0;
                                                              QUERY PLAN                                                               
---------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.00..22.21 rows=5 width=663)
   ->  Nested Loop  (cost=0.00..33788.21 rows=7608 width=663)
         ->  Index Scan using index_bars_on_baz_id on bars  (cost=0.00..658.05 rows=182 width=4)
               Index Cond: (baz_id = 13266)
         ->  Index Scan using index_foos_on_bar_id on foos  (cost=0.00..181.51 rows=42 width=663)
               Index Cond: (foos.bar_id = bars.id)
               Filter: (((NOT foos.privacy_protected) OR (foos.user_id = 67962)) AND ((foos.status)::text = 'DONE'::text))
(7 rows)

//////// Just ORDER
SELECT "foos".*
FROM "foos"
INNER JOIN "bars" ON "foos".bar_id = "bars".id
WHERE (("bars".baz_id = 13266))
ORDER BY "foos"."id" DESC;
                                                              QUERY PLAN                                                               
---------------------------------------------------------------------------------------------------------------------------------------
 Sort  (cost=36515.17..36534.19 rows=7608 width=663)
   Sort Key: foos.id
   ->  Nested Loop  (cost=0.00..33788.21 rows=7608 width=663)
         ->  Index Scan using index_bars_on_baz_id on bars  (cost=0.00..658.05 rows=182 width=4)
               Index Cond: (baz_id = 13266)
         ->  Index Scan using index_foos_on_bar_id on foos  (cost=0.00..181.51 rows=42 width=663)
               Index Cond: (foos.bar_id = bars.id)
               Filter: (((NOT foos.privacy_protected) OR (foos.user_id = 67962)) AND ((foos.status)::text = 'DONE'::text))
(8 rows)

當你同時擁有LIMIT和ORDER BY時,優化器已經決定通過按鍵降序跛行foo上的未過濾記錄會更快,直到它獲得其余條件的五個匹配。 在其他情況下,它只是將查詢作為嵌套循環運行並返回所有記錄。

另一方面,我要說的問題是PG並沒有意識到各種ID的聯合分布,這就是為什么計划如此次優。

對於可能的解決方案:我假設您最近運行過ANALYZE。 如果沒有,請這樣做。 這可以解釋為什么即使對於快速返回的版本,您的估計時間也很高。 如果問題仍然存在,可以將ORDER BY作為子選擇運行,並在外部查詢中打開LIMIT。

可能它會發生,因為在它嘗試命令然后選擇之前。 為什么不嘗試在外部選擇all中對結果進行排序? 類似於:SELECT * FROM(SELECT ... INNER JOIN ETC ...)ORDER BY ... DESC

您的查詢計划表明過濾器

(((NOT privacy_protected) OR (user_id = 67962)) AND ((status)::text = 'DONE'::text))

它沒有出現在SELECT中 - 它來自哪里?

另外,請注意表達式被列為“過濾器”,而不是“索引條件”,這似乎表明沒有應用索引。

它可能正在“foos”上運行全表掃描。 您是否嘗試更改表的順序,而是使用左連接而不是內連接,並查看它是否更快地顯示結果。

說...

SELECT "bars"."id", "foos".*
FROM "bars"
LEFT JOIN "foos" ON "bars"."id" = "foos"."bar_id"
WHERE "bars"."baz_id" = 13266
ORDER BY "foos"."id" DESC
LIMIT 5 OFFSET 0;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM