Redshift/PostgreSQL 中子查询的 GroupAggregate

Posted

技术标签:

【中文标题】Redshift/PostgreSQL 中子查询的 GroupAggregate【英文标题】:GroupAggregate for Subquery in Redshift/PostgreSQL 【发布时间】:2015-10-30 21:48:38 【问题描述】:

我注意到 Redshift 的查询优化器中有一些奇怪的行为,我想知道是否有人可以解释它或指出解决方法。

对于大型 group by 查询,让优化器计划 GroupAggregate 而不是 HashAggregate 非常重要,因此它不会尝试将临时结果放入内存中。总的来说,这对我来说很好。但是当我尝试将 group by 用作子查询时,它会切换到 HashAggregate。

例如,考虑以下查询。

select install_app_version, user_id, max(platform) as plat
from dailies
group by install_app_version, user_id;

每日表有 sortkeys (install_app_version, user_id) 和 distkey (user_id)。因此,GroupAggregate 是可能的,并且查询计划看起来应该是这样的。

XN GroupAggregate  (cost=0.00..184375.32 rows=1038735 width=51)
  ->  XN Seq Scan on daily_players  (cost=0.00..103873.42 rows=10387342 width=51)

相反,如果我在任何其他查询的子查询中使用上述内容,我会得到一个 HashAggregate。例如,即使是像

这样简单的东西
select count(1) from
(   select install_app_version, user_id, max(platform) as plat
    from daily_players
    group by install_app_version, user_id
);

有查询计划

XN Aggregate  (cost=168794.32..168794.32 rows=1 width=0)
  ->  XN Subquery Scan derived_table1  (cost=155810.13..166197.48 rows=1038735 width=0)
        ->  XN HashAggregate  (cost=155810.13..155810.13 rows=1038735 width=39)
              ->  XN Seq Scan on daily_players  (cost=0.00..103873.42 rows=10387342 width=39)

无论我在外部查询中做什么,相同的模式都会持续存在。我可以按 install_app_version 和 user_id 进行分组,我可以进行聚合,我根本无法在外部进行分组。即使对内部查询进行排序也无济于事。

在我展示的情况下,这没什么大不了的,但我正在加入几个带有自己的 group by 的子查询,在上面进行聚合 - 如果没有 GroupAggregate,它很快就会失控并且非常慢。

如果有人对查询优化器有智慧并且可以回答这个问题,我们将不胜感激!谢谢!

【问题讨论】:

【参考方案1】:

不知道你的问题是否仍然存在,但我把它放在这里是因为我认为其他人可能会感兴趣。

Redshift 似乎在默认情况下使用 HashAggregate 执行 GROUP BY 聚合(即使 GroupAggregate 的条件正确),并且仅在聚合进行的至少一项计算需要解决才能返回查询时才切换到 GroupAggregate。我的意思是,在您之前的示例中,“max(platform) as plat”对于查询的最终“COUNT(1)”结果没有用。我相信,在这种情况下,根本不会计算 MAX() 函数的聚合计算。

我使用的解决方法是添加一个无用的 HAVING 子句,它什么都不做,但仍需要计算(例如“HAVING COUNT(1)”)。这始终返回 true(因为每个组的 COUNT(1) 至少等于 1,因此为 true),但允许查询计划使用 GroupAggregate。

例子:

EXPLAIN SELECT COUNT(*) FROM (SELECT mycol FROM mytable GROUP BY 1);

XN Aggregate  (cost=143754365.00..143754365.00 rows=1 width=0)
   ->  XN Subquery Scan derived_table1  (cost=141398732.80..143283238.56 rows=188450576 width=0)
         ->  XN HashAggregate  (cost=141398732.80..141398732.80 rows=188450576 width=40)
               ->  XN Seq Scan on mytable  (cost=0.00..113118986.24 rows=11311898624 width=40)


EXPLAIN SELECT COUNT(*) FROM (SELECT mycol FROM mytable GROUP BY 1 HAVING COUNT(1));

XN Aggregate  (cost=171091871.18..171091871.18 rows=1 width=0)
   ->  XN Subquery Scan derived_table1  (cost=0.00..171091868.68 rows=1000 width=0)
         ->  XN GroupAggregate  (cost=0.00..171091858.68 rows=1000 width=40)
               Filter: ((count(1))::boolean = true)
                ->  XN Seq Scan on mytable  (cost=0.00..113118986.24 rows=11311898624 width=40)

因为 'mycol' 既是 'mytable' 的 distkey 又是 sortkey。

如您所见,查询计划估计比使用 GroupAggregate 的查询比使用 HashAggregate 的查询更昂贵(这一定是使查询计划选择 HashAggregate 的原因)。不要相信,在我的示例中,第二个查询的运行速度比第一个查询快 7 倍!很酷的是 GroupAggregate 不需要太多内存来计算,因此几乎不会执行“基于磁盘的聚合”。

事实上,我意识到使用子查询 GroupAggregate 执行 COUNT(DISTINCT x) 比使用标准 COUNT(DISTINCT x) (在我的示例中,'mycol' 是一个 NOT NULL 列)更好的选择:

EXPLAIN SELECT COUNT(DISTINCT mycol) FROM mytable ;

XN Aggregate  (cost=143754365.00..143754365.00 rows=1 width=72)
->  XN Subquery Scan volt_dt_0  (cost=141398732.80..143283238.56 rows=188450576 width=72)
         ->  XN HashAggregate  (cost=141398732.80..141398732.80 rows=188450576 width=40)
               ->  XN Seq Scan on mytable  (cost=0.00..113118986.24 rows=11311898624 width=40)

3 分 46 秒

EXPLAIN SELECT COUNT(*) FROM (SELECT mycol FROM mytable GROUP BY 1 HAVING COUNT(1));

XN Aggregate  (cost=171091871.18..171091871.18 rows=1 width=0)
   ->  XN Subquery Scan derived_table1  (cost=0.00..171091868.68 rows=1000 width=0)
         ->  XN GroupAggregate  (cost=0.00..171091858.68 rows=1000 width=40)
               Filter: ((count(1))::boolean = true)
               ->  XN Seq Scan on mytable  (cost=0.00..113118986.24 rows=11311898624 width=40)

40 秒

希望有帮助!

【讨论】:

以上是关于Redshift/PostgreSQL 中子查询的 GroupAggregate的主要内容,如果未能解决你的问题,请参考以下文章

如何(以编程方式)知道何时在 PostgreSQL/Amazon Redshift 上完成查询?

按百分位数将类似 sql 的查询的结果分组:在 Redshift / postgresql

在 redshift 中使用正则表达式

RedShift / PostgreSQL 中串行类型的替代方案

psycopg2/python 将数据从 postgresql 复制到 Amazon RedShift(postgresql)

使用 Amazon Redshift / PostgreSQL 进行队列分析