簡體   English   中英

PostgreSQL非常慢的索引掃描

[英]PostgreSQL very slow index scan

我們的PostgreSQL數據庫包含以下表格:

  • 類別

     id SERIAL PRIMARY KEY name TEXT 
  • 用品

     id SERIAL PRIMARY KEY content TEXT 
  • Categories_articles(多對多關系)

     category_id INT REFERENCES categories (id) article_id INT REFERENCES articles (id) UNIQUE (category_id, article_id) 
  • 評論

     article_id INT REFERENCES articles (id) posted_date TIMESTAMP NOT NULL is_visible BOOLEAN NOT NULL is_banned BOOLEAN NOT NULL message TEXT 

我們在comments表上有部分索引:

CREATE INDEX comments_posted_date_idx
ON comments USING btree (posted_date)
WHERE is_visible = TRUE AND is_banned = FALSE;

因此,我們需要按類別獲取最近的評論:

SELECT * FROM comments co
JOIN categories_articles ca
    ON ca.article_id = co.article_id
WHERE ca.category_id = 1
    AND co.is_visible = TRUE
    AND co.is_banned = FALSE
ORDER BY co.posted_date DESC
LIMIT 20;

EXPLAIN ANALYZE輸出:

Limit  (cost=0.00..1445.20 rows=20 width=24) (actual time=93969.479..98515.109 rows=20 loops=1)
  ->  Nested Loop  (cost=0.00..7577979.47 rows=104871 width=24) (actual time=93969.475..98515.084 rows=20 loops=1)
        ->  Index Scan Backward using comments_posted_date_idx on comments co  (cost=0.00..3248957.69 rows=9282514 width=40) (actual time=13.405..82860.852 rows=117881 loops=1)
        ->  Index Scan using categories_articles_article_id_idx on categories_articles ca  (cost=0.00..0.45 rows=1 width=16) (actual time=0.132..0.132 rows=0 loops=117881)
              Index Cond: (article_id = co.article_id)
              Filter: (category_id = 1)
Total runtime: 98515.179 ms

有什么方法可以優化查詢?

UPD:表格comments有大約1100萬行。

這是一個病理計划,其中實際上並沒有良好的解決方法……簡而言之,查找行的選項基本上是:

  • 以相反的順序posted_date上的索引,並使用article_id嵌套連接,直到找到20個匹配項為止-在過程中掃描表的很大部分,因為沒有太多行匹配(如現在所做的那樣),然后停止; 要么

  • 通過對category_id的索引進行遍歷,對article_id進行嵌套或哈希article_id以查找所有匹配的注釋,並對前20條注釋進行top-n排序。

如果您有很多文章,那么第一篇文章會更快。 如果您人數很少,第二個將會是。 麻煩的是,Postgres沒有收集相關的統計信息。 它只是在做假設,不一定是好的假設。

可能能夠對此部分進行更快的索引掃描:

Index Cond: (article_id = co.article_id)
    Filter: (category_id = 1)

通過添加反向(也是唯一的)指數(article_id, category_id)categories_articles表,而不是在普通(article_id) -你忘了你的問題提了,但仍然會出現在你的計划。

不管有沒有它,也可以嘗試在comments表上的(article_id, posted_date)(posted_date, article_id)上使用(部分)索引,而不是普通的(posted_date)

由於EXPLAIN輸出僅顯示索引掃描,所以真正的問題是:時間在哪里? 我想當然地猜測您的磁盤IO已飽和,您可以通過運行“ iostat 1”或類似的工具並查看%busy計數器是否為100%或(如果沒有這樣的計數器)來查看您的“等待”,以進行驗證“ CPU狀態接近100%。

(category_id,postd_date)的索引有什么問題? 我假設您始終有一個category_id用於搜索?

學習查詢計划者時,絕對不要使用限制。 該關鍵字完全改變了查詢計划程序,請參閱: http : //www.postgresql.org/docs/9.1/static/queries-limit.html因此,我不建議您花費時間來改進說明分析。

嘗試使用以下設置:work_mem Effective_cache_size

您可以嘗試重寫該查詢以擺脫嵌套循環。 我會給您一些示例,其中一個可行,也許沒有,但是您會得到一些想法。

SELECT * 
FROM comments co
JOIN categories_articles ca
    ON ca.article_id = co.article_id and ca.category_id = 1
WHERE   co.is_visible = TRUE
    AND co.is_banned = FALSE
ORDER BY co.posted_date DESC

with comments as (
select * -- Better with only THE FIELDS YOU NEED
from comments 
where co.is_visible = TRUE
and   co.is_banned = FALSE
) 
select * 
from comments co
join categories_articles ca
on ca.article_id = co.article_id
ORDER BY co.posted_date DESC

暫無
暫無

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

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