greenplum/postgreSQL 中的 count(*) 和 count(*) over () 性能差异

问题描述

我想查询明细数据和明细数据总数。一般来说,这需要两句sql。例如,一个select col1,col2,col3 from tb limit 50 offset 0,另一个select count() from tb。为了减少查询次数,我改用了“select col1,col3,count() over () totol_count from tb”。

但我发现后者(select col1,count(*) over () totol_count from tb limit 50 offset 0)有时比前者慢得多。为什么? 两种方法性能有什么区别?

这是解释分析。我只是把不同的放在上面。

  1. select col1 from tb limit 20 offset 0 限制(cost=743828.93..747114.71 rows=200 width=287)(实际时间=2290.254..2390.969 rows=200 loops=1) -> Gather Motion 20:1 (slice3;segments: 20) (cost=743828.93..747114.71 rows=200 width=287) (实际时间=2290.248..2390.933 rows=200 loops=1) -> 限制(cost=743828.93..747110.71 rows=10 width=287)(实际时间=2285.350..2385.507 rows=200 loops=1)

  2. 从 tb 中选择 count(*) 合计(cost=6123212.33..6123212.34 rows=1 width=8)(实际时间=2339.615..2339.616 rows=1 loops=1) -> Gather Motion 20:1 (slice2;segments: 20) (cost=6123212.10..6123212.32 rows=1 width=8) (actual time=635.672..2339.433 rows=20 loops=1) -> 聚合(成本=6123212.10..6123212.11 行=1 宽度=8)(实际时间=633.879..633.879 行=1 循环=1)

  3. select col,count(*) over() from tb limit 20 offset 0 限制(cost=743828.93..747113.21 rows=200 width=287)(实际时间=57787.942..57788.339 rows=200 loops=1) -> WindowAgg (cost=743828.93..307341631.82 rows=18670608 width=287) (实际时间=57787.936..57788.310 rows=200 loops=1) -> Gather Motion 20:1 (slice3;segments: 20) (cost=743828.93..307014896.18 rows=18670608 width=287) (实际时间=1514.368..32796.802 rows=1873513个循环)

解决方法

只有在看到您的执行计划后,才能确定哪个更适合您。

但是如果您运行 Analyze 足够多,您可以使用:

SELECT reltuples::bigint AS estimate
FROM   pg_class
WHERE  oid = 'tb'::regclass;

它非常快,但不会为您提供准确的计数,而是近似计数。准确性将取决于运行分析的频率。

select count(*) from tb 慢的原因与MVCC有关 PostgreSQL 中的实现。多个交易可以的事实 看到数据的不同状态意味着不能有 “COUNT(*)” 汇总整个数据的简单方法 桌子;在某种意义上,PostgreSQL 必须遍历所有行。这 通常会导致顺序扫描读取有关每个的信息 表中的行。 Slow counting in PostgreSQL