GroupAggregate for Redquift / PostgreSQL中的子查询

问题描述 投票:3回答:1

我注意到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,它会很快失控并且非常慢。

如果有人对查询优化器有所了解并且可以回答这个问题,那就非常感谢了!谢谢!

sql query-optimization amazon-redshift query-performance
1个回答
0
投票

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

Redshift默认情况下使用HashAggregate执行GROUP BY聚合(即使GroupAggregate的条件是正确的),并且当至少有一个由聚合进行的计算需要解析为QUERY TO RETURN时,只切换到GroupAggregate。我的意思是,在您之前的示例中,“max(platform)as plat”对查询的最终“COUNT(1)”结果没有用处。我相信,在这种情况下,根本不计算MAX()函数的聚合计算。

我使用的解决方法是添加一个无用的HAVING子句,它只做任何事情但仍需要计算(例如“HAVING COUNT(1)”)。这总是返回true(因为每个组的COUNT(1)等于至少为1,因此是真的),但是使查询计划能够使用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'既是disttable又是'mytable'的排序键。

如您所见,查询计划估计比使用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秒

希望有所帮助!

© www.soinside.com 2019 - 2024. All rights reserved.