我注意到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,它会很快失控并且非常慢。
如果有人对查询优化器有所了解并且可以回答这个问题,那就非常感谢了!谢谢!
不知道你的问题是否仍然存在,但我把它放在这里因为我认为其他人可能会感兴趣。
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秒
希望有所帮助!