![](/img/trans.png)
[英]PostgreSQL Bitmap Heap Scan on index is very slow but Index Only Scan is fast
[英]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.