我们的 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 个匹配项 — 扫描表中的很大一部分process 因为没有那么多行匹配,就像它现在所做的那样 - 并停止;或者运行索引,例如
category_id
,在article_id
上嵌套或散列连接以查找所有匹配的评论,并对前 20 条评论进行前 n 排序。
如果您有很多文章,第一个会更快。如果你很少,第二个将是。问题是,Postgres 不收集相关统计数据;它在做假设,但不一定是好的假设。
您可能能够对该部分进行更快的索引扫描:
Index Cond: (article_id = co.article_id)
Filter: (category_id = 1)
通过在 categories_articles
表的 (article_id, category_id)
上添加反向(和唯一)索引,而不是在普通的 (article_id)
上— 您忘记在问题中提及,但仍出现在您的计划中。
无论有没有它,也可以尝试在 (article_id, posted_date)
和 (posted_date, article_id)
上对 comments
建立(部分)索引> 表格,而不是普通的 (posted_date)
。
关于sql - PostgreSQL 非常慢的索引扫描,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21432747/