我构建了一个简单的应用程序,用于使用rails和postgresql阅读RSS提要,但是当我尝试在feed_entries
表中查询来自多个提要的帖子时遇到了性能问题。示例查询如下所示,以检索给定的feed ID集合的20个最新条目:
SELECT * FROM feed_entries WHERE feed_id IN (19, 21, 383, 1867, 3103) ORDER BY published_at DESC LIMIT 20;
feed_entries
表中有大约400万行,使用Fugu计划在Heroku Postgres上托管,它有一些索引,包括:
"index_feed_entries_on_feed_id_and_published_at" btree (feed_id, published_at)
"index_feed_entries_on_published_at" btree (published_at)
以下是查询规划器的结果:
EXPLAIN ANALYZE SELECT * FROM feed_entries WHERE feed_id IN (19, 21, 383, 1867, 3103) ORDER BY published_at DESC LIMIT 20;
Limit (cost=4353.93..4353.94 rows=20 width=1016) (actual time=12172.275..12172.325 rows=20 loops=1)
-> Sort (cost=4353.93..4355.07 rows=2286 width=1016) (actual time=12172.268..12172.284 rows=20 loops=1)
Sort Key: published_at
Sort Method: top-N heapsort Memory: 52kB
-> Index Scan using index_feed_entries_on_feed_id_and_published_at on feed_entries (cost=0.00..4341.76 rows=2286 width=1016) (actual time=8.612..12169.504 rows=630 loops=1)
Index Cond: (feed_id = ANY ('{19,21,383,1867,3103}'::integer[]))
Total runtime: 12172.520 ms
计划程序看起来正在使用适当的索引,但是扫描索引仍然需要大约12秒,这对于有400万行的表来说太长了。如果我完全按照上面那样重复查询规划器,那么第二次它告诉我整个事情只需要2毫秒,也许这只是因为第一个查询的结果被缓存,但它仍然让我感到困惑。我还尝试在运行查询之前运行VACUUM ANALYZE
,但它没什么区别。另外,如果我在表中查询单个feed_id,那么查询规划器使用Index Scan Backward using index_feed_entries_on_feed_id_and_published_at on feed_entries
,总执行时间要快得多,大约20ms。
我是否可以采用其他策略来优化这种相对简单的IN查询的性能?
答案 0 :(得分:1)
要尝试的另一件事是这种替代查询形式:
SELECT *
FROM feed_entries
JOIN (unnest('{19,21,383,1867,3103}'::int[]) AS feed_id) sub USING (feed_id)
ORDER BY published_at DESC
LIMIT 20;
multi-column indexes中列的排序顺序很重要。使用:
CREATE index_feed_entries_2 ON feed_entries (feed_id, published_at DESC)
如果你根据这个索引CLUSTER
你的表,这可能会给你一点点提升,但是随着大量的更新,效率会下降。有关详细信息,请阅读此相关答案的最后一章:
Bitmap Heap Scan performance
答案 1 :(得分:0)
尝试使用DESC订单创建索引。 例如
create index feed_entries_published_at_desc_idx on feed_entries ( published_at desc ) with (fillfactor=100);
你可以在(feed_id,published_at desc)上尝试类似(复合)索引,看看它是如何工作的。