PostgreSQL:DISTINCT中的最新行性能低于GROUP BY中的最大行

PostgreSQL:DISTINCT中的最新行性能低于GROUP BY中的最大行,postgresql,group-by,postgresql-10,distinct-on,Postgresql,Group By,Postgresql 10,Distinct On,我想更好地了解一个情况: 我有一个表t,有两行和一个索引: CREATE TABLE t ( refid BIGINT NOT NULL, created TIMESTAMPTZ NOT NULL ); CREATE INDEX t_refid_created ON t (refid, created); 为了获得每个不同的refid的最新(创建的值最高)行,我编写了两个查询: -- index only scan t_ref

我想更好地了解一个情况:

我有一个表
t
,有两行和一个索引:

CREATE TABLE t (
  refid                 BIGINT NOT NULL,
  created               TIMESTAMPTZ NOT NULL
);
CREATE INDEX t_refid_created ON t (refid, created);
为了获得每个不同的
refid
的最新(创建的
值最高)行,我编写了两个查询:

-- index only scan t_refid_created_desc_idx
SELECT DISTINCT ON (refid) * FROM t
ORDER BY refid, created DESC;

-- index scan t_refid_created_idx 
SELECT refid, max(created) FROM t GROUP BY refid;
t
大约有1600万行,并且
refid
中的方差约为500个不同的值时,第二个查询返回的速度远远快于第二个查询

起初我认为,因为我是按
created DESC
排序的,所以它需要进行向后索引扫描,并且从一个方差较大的值(created)开始。因此,我添加了以下索引:

CREATE index t_refid_created_desc_idx ON t (refid, created DESC);
它确实被使用了(而不是上一个索引的向后扫描),但没有任何改进

如果我理解正确,第二个查询将通过
refid
进行聚合,然后扫描每个聚合以找到max
created
值。这听起来像是很多工作。 据我所知,第一个查询应该简单地迭代索引的第一部分,然后对于每个
refid
,它应该使用索引的第二部分,取第一个值

显然情况并非如此,
selectdistinct
query所用的时间是
groupby
的两倍

我错过了什么

以下是第一次和第二次查询的
EXPLAIN ANALYZE
输出:

Unique  (cost=0.56..850119.78 rows=291 width=16) (actual time=0.103..13414.913 rows=469 loops=1)
  ->  Index Only Scan using t_refid_created_desc_idx on t  (cost=0.56..808518.47 rows=16640527 width=16) (actual time=0.102..12113.454 rows=16640527 loops=1)
        Heap Fetches: 16640527
Planning time: 0.157 ms
Execution time: 13415.047 ms
第一个查询大约在10秒内运行,而第二个查询在2秒内获得相同的结果!甚至不用索引


我使用PostgreSQL 10.5。< /P> < P>我不能回答谜语,为什么<代码> > <代码>不考虑第二个计划。从成本估算中我们可以看出,PostgreSQL认为它更便宜

我想没有人实现过将
DISTINCT
下推到并行计划中。你可以问邮件列表

然而,第一个查询的问题是1600万堆回迁。这意味着这实际上是一个正常的索引扫描!从规划者的角度看,这似乎是一个错误的估计


如果我是对的,清洁可见性地图的表上的
真空
将大大改进第一次查询。

请使用
解释分析
发布结果,以便我们可以查看实际时间和行统计信息(而不仅仅是估计值)。我用
EXPLAIN ANALYZE
输出更新了这个问题。
VACUUM
确实提高了第一个查询的性能,只是比第二个查询差了一点。我不明白,如果它有索引并使用它,为什么要对每一行执行堆提取。根据我的理解,拥有索引意味着我们有一个对
refid
的每个分区的引用,其中的行按
created DESC
排序。如果是这样,那么工作应该只依赖于不同的
refid
值的数量。没有堆获取,对吗?必须是“未实现”的情况。在
真空
之后,
解释分析
在第一个查询中给出:“唯一的(成本=0.56..355285.78行=291宽度=16)(实际时间=0.038..4599.770行=469循环=1)”->仅使用效果扫描索引(成本=0.56..313684.47行=16640527宽度=16)(实际时间=0.037..3346.267行=16640527循环=1)“堆取数:0”“计划时间:0.102毫秒”“执行时间:4599.847毫秒”PostgreSQL中没有“跳过扫描”。需要进行堆取数以确定索引项是否可见(该信息仅在堆中).也许随机访问在您的存储上非常昂贵?
Finalize GroupAggregate  (cost=599925.13..599932.41 rows=291 width=16) (actual time=3454.350..3454.884 rows=469 loops=1)
  Group Key: refid
  ->  Sort  (cost=599925.13..599926.59 rows=582 width=16) (actual time=3454.344..3454.509 rows=1372 loops=1)
        Sort Key: refid
        Sort Method: quicksort  Memory: 113kB
        ->  Gather  (cost=599837.29..599898.40 rows=582 width=16) (actual time=3453.194..3560.602 rows=1372 loops=1)
              Workers Planned: 2
              Workers Launched: 2
              ->  Partial HashAggregate  (cost=598837.29..598840.20 rows=291 width=16) (actual time=3448.225..3448.357 rows=457 loops=3)
                    Group Key: refid
                    ->  Parallel Seq Scan on t  (cost=0.00..564169.53 rows=6933553 width=16) (actual time=0.047..2164.459 rows=5546842 loops=3)
Planning time: 0.157 ms
Execution time: 3561.727 ms