postgresql COUNT(DISTINCT ...) 非常慢
Posted
技术标签:
【中文标题】postgresql COUNT(DISTINCT ...) 非常慢【英文标题】:postgresql COUNT(DISTINCT ...) very slow 【发布时间】:2012-06-30 07:18:18 【问题描述】:我有一个非常简单的 SQL 查询:
SELECT COUNT(DISTINCT x) FROM table;
我的表有大约 150 万行。这个查询运行得很慢;大约需要 7.5 秒,相比之下
SELECT COUNT(x) FROM table;
大约需要 435 毫秒。有什么方法可以更改我的查询以提高性能?我尝试分组并定期计数,以及在 x 上放置索引;两者都有相同的 7.5 秒执行时间。
【问题讨论】:
我不这么认为。获取 150 万行的不同值会很慢。 我刚刚在 C# 中尝试过,从内存中获取 150 万个 整数 的不同值在我的计算机上花费了超过一秒钟的时间。所以我认为你可能不走运。 查询计划在很大程度上取决于表结构(索引)和调整常量(work)mem、effective_cache_size、random_page_cost)的设置。通过合理的调整,查询可能会在不到一秒的时间内执行。 您能说得更具体些吗?在一秒钟内获得它需要哪些索引和调整常量?为简单起见,假设这是一个双列表,第一列 y 上有一个主键,我正在对 int 类型的第二列 x 执行此“不同”查询,有 150 万行。 请包含所有索引的表定义(psql
的\d
输出是好的),并准确列出您有问题的列。很高兴看到两个查询的EXPLAIN ANALYZE
。
【参考方案1】:
你可以用这个:
SELECT COUNT(*) FROM (SELECT DISTINCT column_name FROM table_name) AS temp;
这比:
COUNT(DISTINCT column_name)
【讨论】:
圣问蝙蝠侠!这加快了我的 postgres 计数从 190 到 4.5 哇! 我在www.postgresql.org 上找到了这个帖子,讨论了同样的事情:link。其中一个回复(由 Jeff Janes 提供)说 COUNT(DISTINCT()) 对表进行排序以完成其工作,而不是使用哈希。 @Ankur 我可以问你问题吗?由于COUNT(DISTINCT())
执行排序,因此在column_name
上建立索引肯定会有所帮助,尤其是在work_mem
数量相对较少的情况下(其中散列会产生相对大量的批次)。从那以后,使用 COUNT (DISTINCT()_,不是吗?
@musmahn Count(column)
只计算非空值。 count(*)
计算行数。所以第一个/更长的,也将计算空行(一次)。更改为 count(column_name)
以使它们的行为相同。
@ankur 这对我没有多大用处..没有得到任何显着的改进。【参考方案2】:
-- My default settings (this is basically a single-session machine, so work_mem is pretty high)
SET effective_cache_size='2048MB';
SET work_mem='16MB';
\echo original
EXPLAIN ANALYZE
SELECT
COUNT (distinct val) as aantal
FROM one
;
\echo group by+count(*)
EXPLAIN ANALYZE
SELECT
distinct val
-- , COUNT(*)
FROM one
GROUP BY val;
\echo with CTE
EXPLAIN ANALYZE
WITH agg AS (
SELECT distinct val
FROM one
GROUP BY val
)
SELECT COUNT (*) as aantal
FROM agg
;
结果:
original QUERY PLAN
----------------------------------------------------------------------------------------------------------------------
Aggregate (cost=36448.06..36448.07 rows=1 width=4) (actual time=1766.472..1766.472 rows=1 loops=1)
-> Seq Scan on one (cost=0.00..32698.45 rows=1499845 width=4) (actual time=31.371..185.914 rows=1499845 loops=1)
Total runtime: 1766.642 ms
(3 rows)
group by+count(*)
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------
HashAggregate (cost=36464.31..36477.31 rows=1300 width=4) (actual time=412.470..412.598 rows=1300 loops=1)
-> HashAggregate (cost=36448.06..36461.06 rows=1300 width=4) (actual time=412.066..412.203 rows=1300 loops=1)
-> Seq Scan on one (cost=0.00..32698.45 rows=1499845 width=4) (actual time=26.134..166.846 rows=1499845 loops=1)
Total runtime: 412.686 ms
(4 rows)
with CTE
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=36506.56..36506.57 rows=1 width=0) (actual time=408.239..408.239 rows=1 loops=1)
CTE agg
-> HashAggregate (cost=36464.31..36477.31 rows=1300 width=4) (actual time=407.704..407.847 rows=1300 loops=1)
-> HashAggregate (cost=36448.06..36461.06 rows=1300 width=4) (actual time=407.320..407.467 rows=1300 loops=1)
-> Seq Scan on one (cost=0.00..32698.45 rows=1499845 width=4) (actual time=24.321..165.256 rows=1499845 loops=1)
-> CTE Scan on agg (cost=0.00..26.00 rows=1300 width=0) (actual time=407.707..408.154 rows=1300 loops=1)
Total runtime: 408.300 ms
(7 rows)
可能也可以通过其他方法(窗口函数)生成与 CTE 相同的计划
【讨论】:
你考虑过缓存的效果吗?如果随后进行三个“解释分析”,第一个可能会从磁盘中缓慢获取内容,而后两个可能会从内存中快速获取。 确实:effective_cache_size 是第一个需要调整的设置。我的是 2GB,IIRC。 我将 Effective_cache_size 设置为 2GB,性能没有变化。您建议调整任何其他设置吗?如果是,那是什么? 1) 如何你是怎么设置的? (你 HUP 了吗?) 2)你真的有那么多可用的内存吗? 3) 向我们展示您的计划。 4)也许我的机器更快,或者你的机器有更多的并发负载要处理。 @ferson2020:好的 我用语句设置它:SET effective_cache_size='2GB';我确实有那么多可用的内存。我尝试包含我的查询计划,但它不适合评论框。【参考方案3】:如果您的count(distinct(x))
比count(x)
慢得多,那么您可以通过使用触发器在不同的表(例如table_name_x_counts (x integer not null, x_count int not null)
)中维护x 值计数来加速此查询。但是您的写入性能会受到影响,如果您在单个事务中更新多个 x
值,那么您需要以某种明确的顺序执行此操作以避免可能的死锁。
【讨论】:
【参考方案4】:我也在寻找相同的答案,因为有时我需要 total_count 具有不同的值以及限制/偏移量。
因为它有点棘手 - 获得具有不同值的总计数以及限制/偏移量。通常很难通过限制/偏移来获得总计数。终于有办法了——
SELECT DISTINCT COUNT(*) OVER() as total_count, * FROM table_name limit 2 offset 0;
查询性能也很高。
【讨论】:
【参考方案5】:select coluna, count(coluna) as qtd from tabela group by coluna
【讨论】:
以上是关于postgresql COUNT(DISTINCT ...) 非常慢的主要内容,如果未能解决你的问题,请参考以下文章
"HybridDB · 性能优化 · Count Distinct的几种实现方式” 读后感
在插入 table2 之前,如何在 table1 的多个列上应用 count 和 distinct
为啥 distinct 不能与 Laravel + Postgresql 一起使用?