我有一个非常简单的表格
CREATE TABLE approved_posts (
project_id INTEGER,
feed_id INTEGER,
post_id INTEGER,
approved_time TIMESTAMP NOT NULL,
post_time TIMESTAMP NOT NULL,
PRIMARY KEY (project_id, feed_id, post_id)
)
我正在尝试优化这个查询:
SELECT *
FROM approved_posts
WHERE feed_id IN (?, ?, ?)
AND project_id = ?
ORDER BY approved_time DESC, post_time DESC
LIMIT 1;
查询优化器会获取与谓词匹配的每个“approved_post”,将所有100k结果排序,并返回它找到的顶部结果。
我确实在“project_id,feed_id,approved_time,post_time”上有索引,如果我删除“post_time”的排序,或者用单个“=?”替换“IN(?,?,?)”,则查询优化器将使用该索引。然后它只需进行反向索引扫描即可获得第一个结果,速度非常快。
选项A:
Limit (cost=0.43..6.57 rows=1 width=24) (actual time=0.101..0.101 rows=1 loops=1)
-> Index Scan Backward using approved_posts_approved_time_idx on approved_posts p (cost=0.43..840483.02 rows=136940 width=24) (actual time=0.100..0.100 rows=1 loops=1)
Filter: (feed_id = ANY ('{73321,73771,73772,73773,73774}'::integer[]))
Rows Removed by Filter: 37
Total runtime: 0.129 ms
选项B:
Limit (cost=0.43..3.31 rows=1 width=24) (actual time=0.065..0.065 rows=1 loops=1)
-> Index Scan Backward using approved_posts_full_pagination_index on approved_posts p (cost=0.43..126884.70 rows=44049 width=24) (actual time=0.063..0.063 rows=1 loops=1)
Index Cond: ((project_id = 148772) AND (feed_id = 73321))
Total runtime: 0.092 ms
但是如果没有这些调整,它的性能就不太好...
Limit (cost=169792.16..169792.17 rows=1 width=24) (actual time=510.225..510.225 rows=1 loops=1)
-> Sort (cost=169792.16..170118.06 rows=130357 width=24) (actual time=510.224..510.224 rows=1 loops=1)
Sort Key: approved_time, post_time
Sort Method: top-N heapsort Memory: 25kB
-> Bitmap Heap Scan on approved_posts p (cost=12324.41..169140.38 rows=130357 width=24) (actual time=362.210..469.387 rows=126260 loops=1)
Recheck Cond: (feed_id = ANY ('{73321,73771,73772,73773,73774}'::integer[]))
-> Bitmap Index Scan on approved_posts_feed_id_idx (cost=0.00..12291.82 rows=130357 width=0) (actual time=354.496..354.496 rows=126260 loops=1)
Index Cond: (feed_id = ANY ('{73321,73771,73772,73773,73774}'::integer[]))
Total runtime: 510.265 ms
我甚至可以在这五个feed id上添加一个条件索引,它将再次执行正确的操作。
我目前最好的解决方案是将每个feed_id放入自己的查询中,并在它们之间进行大量的UNION。但是这种方法不太可扩展,因为我可能想要从30个feeds中选择前500个,拉取15k行并无故排序。此外,使用这种策略管理偏移量有些复杂。
有人知道如何在我的良好索引数据上执行这个带有两个排序的IN子句,并让Postgres执行正确的操作吗?
我正在使用Postgres 9.3.3。以下是我的索引:
"approved_posts_project_id_feed_id_post_id_key" UNIQUE CONSTRAINT, btree (project_id, feed_id, post_id)
"approved_posts_approved_time_idx" btree (approved_time)
"approved_posts_feed_id_idx" btree (feed_id)
"approved_posts_full_pagination_index" btree (project_id, feed_id, approved_time, post_time)
"approved_posts_post_id_idx" btree (post_id)
"approved_posts_post_time_idx" btree (post_time)
"approved_posts_project_id_idx" btree (project_id)
所有列都不能为空。
这个表有2百万行,分别属于200个feed ID和19个项目ID。
以下是最常见的Feed ID:
feed_id | count
---------+--------
73607 | 558860
73837 | 354018
73832 | 220285
73836 | 172664
73321 | 118695
73819 | 95999
73821 | 75871
73056 | 65779
73070 | 54655
73827 | 43710
73079 | 36700
73574 | 36111
73055 | 25682
73072 | 22596
73589 | 19856
73953 | 15286
73159 | 13059
73839 | 8925
关于每个feedid
/projectid
配对的最小值/最大值/平均基数,我们有以下数据:
min | max | avg
-----+--------+-----------------------
1 | 559021 | 9427.9140271493212670
9.3.3
提出了一个问题:为什么不至少是9.3.9(如果无法选择9.4的话)?我们始终建议所有用户运行当前使用的主要版本的最新可用次要发布版本。 - Erwin Brandstetter