我想更好地了解一种情况:
我有一个表t
,其中有两行和一个索引:
CREATE TABLE t (
refid BIGINT NOT NULL,
created TIMESTAMPTZ NOT NULL
);
CREATE INDEX t_refid_created ON t (refid, created);
为了获得每个不同的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
进行排序,因此需要从具有高方差的值开始(创建)进行向后索引扫描。所以我添加了以下索引:
CREATE index t_refid_created_desc_idx ON t (refid, created DESC);
确实使用了它(而不是对先前索引的向后扫描),但是没有任何改善。
如果我理解正确,第二个查询将按refid
进行汇总,然后扫描每个汇总以找到最大created
值。这听起来像很多工作。
据我所知,第一个查询应该只对索引的第一部分进行迭代,然后对于每个refid
,它应该使用索引的第二部分,并采用第一个值。
显然不是这种情况,SELECT DISTINCT
查询的时间是GROUP BY
的两倍。
我在这里想念什么?
以下是第一个和第二个查询的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
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
第一个查询运行大约10秒钟,而第二个查询则在2秒钟内达到相同的结果!而且甚至不用索引!
我正在使用PostgreSQL 10.5。
答案 0 :(得分:1)
我无法回答为什么DISTINCT ON
不考虑第二个计划的谜语。从成本估算中,我们发现PostgreSQL认为它更便宜。
我猜没有人实施过将DISTINCT
下推到并行计划中。您可以询问邮件列表。
但是,第一个查询的问题是1600万次堆提取。这意味着这实际上是正常的索引扫描!在计划者看来,这似乎是一个错误的估计。
如果我是对的,那么清理{em>可见性图的桌子上的VACUUM
应该会大大改善第一个查询。