为啥一个 group by 的聚合这么慢?

Posted

技术标签:

【中文标题】为啥一个 group by 的聚合这么慢?【英文标题】:Why is an aggregation with a group by so slow?为什么一个 group by 的聚合这么慢? 【发布时间】:2021-10-17 09:55:39 【问题描述】:

我在 Redshift 实例中有一个表 [order],行数为 780 000。 在表上运行以下 group by 子句需要 60 多秒。 在 MSSQL 中,完全相同的查询需要 1 秒。 任何关于 Redshift 为什么需要这么长时间以及如何改进查询的建议都将不胜感激。

select
    salesorderid
    ,max(orderid)           as max_order_id
    ,min(latestdelivery)    as min_latestdelivery
    ,max(latestdelivery)    as max_latestdelivery
    ,min(sourceid)          as min_sourceid
    ,max(sourceid)          as max_sourceid
    ,min(salesitem)         as min_salesitem
    ,max(salesitem)         as max_salesitem
    ,min(qty)               as min_qty
    ,max(qty)               as max_qty
    ,min(weight)            as min_weight
    ,max(weight)            as max_weight
    ,min(refb)              as min_refb
    ,max(refb)              as max_refb
    ,min(blocked)           as min_blocked
    ,max(blocked)           as max_blocked
    ,min(updatemode)        as min_updatemode
from public.order o
where o.datecreated >= getdate() - interval '24 month'
group by salesorderid;

解释:

XN HashAggregate  (cost=35513.57..52310.29 rows=419918 width=99)
->  XN Seq Scan on "order" o  (cost=0.00..9738.60 rows=606470 width=99)
Filter: (datecreated >= '2019-10-17 11:52:14'::timestamp without time zone)

【问题讨论】:

您是如何插入数据的?用什么命令? 桌子上的 SORTKEY 是什么? @JohnRotenstein +1 - 还有:DISTKEY 是什么? 你的salesorderids 是独一无二的吗? 很遗憾我没有插入数据,所以我无法提供插入命令。 SORTKEY 已启用(salesorderid,datecreated)。 DISTKEY 已启用(创建日期)。 salesorderis 不是唯一的。 【参考方案1】:

虽然这里有几处不理想的地方,但可能需要 1 分钟的原因不在 SQL 中。我会逐项列出我稍后看到的问题。

您的源表少于 1M 行 - 按照 Redshift 标准来说非常小。排序顺序或元数据陈旧不太可能导致大问题。该查询是一个简单的聚合,解释计划显示 Redshift 预计此“应该”花费不到一秒的时间。所有这些都让我将注意力集中在外部混杂因素上。

我将假设您是从工作台测量查询时间,而不是通过系统表数据。如果不是这种情况,请告诉我们。如果是这样,您正在查看数据库的完整往返行程,而不仅仅是执行时间。完成查询需要执行多个步骤。它需要通过 WLM 队列进行编译、调度、执行和返回结果。这些步骤中的任何一个都可能需要很长时间,每个步骤都有很多原因。

因此,第一步是找出查询在每个步骤中所用的时间。我喜欢从中间开始(排队和执行,因为这通常是事情变慢的地方,但并非总是如此)。 STL_WLM_QUERY 系统表将显示您的查询通过队列花费了多长时间以及执行时间。一种可能性是您的查询在队列中等待而挂起,您应该这样做。它还将显示总执行时间,以便您查看运行所需的时间(这是 EXPLAIN 计划的全部重点)。可能这里有问题,但我会继续阅读。

如果事情快速通过队列并在合理的时间内执行,那么我们需要查看编译并返回。 SVL_COMPILE 将显示查询编译所用的时间,但鉴于此查询的简单性,这不太可能是您的问题所在。

可能的原因在于返回步骤。 EXPLAIN 期望您收到 419K 行有意义的宽度 - 这可能代表相当数量的数据。如果您与 Redshift 和您自己之间的网络带宽有限,那么这些数据量可能会占用您运行时间的很大一部分来传输。 STL_RETURN 从 Redshift 的角度保存这一步的数据。您还可以通过将结果放入临时表中重新运行查询来测试这一点,看看这是否大大提高了运行时间。这只是我的猜测。

正确的做法是收集有关时间花费的数据并根据这些信息采取行动。如果此时需要更多指导,请报告此问题。

现在对查询本身进行一些观察。我建议您不要使用时间戳作为表的分布键。您不太可能通过时间戳加入或分组,这对执行查询没有帮助。由于您按 salesorderid 分组,这可能是分发键的不错选择。此外,您应该使用常见的 WHERE 子句列作为排序键。您已完成此操作,但将其设置为 salesorderid 之后的第二个排序键。这可能会窃取 datecreated 的所有功能并阻止它提供任何好处。我建议您只使用 salesorderid 作为此排序键,但这是基于查看您的查询中的一个,因此将其视为一组信息不足的建议。这一切都表明,鉴于桌子的尺寸很小,这一切都不太可能产生很大的不同。这里没有足够的数据来让这些键产生巨大的影响。

【讨论】:

以上是关于为啥一个 group by 的聚合这么慢?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 CROSS APPLY 与列和聚合函数需要 Group by

为啥没有聚合的结束 Group By 会减慢我的查询速度?

为啥我的 MongoDB 聚合查询这么慢

为啥没有聚合函数的选择列需要成为 MySQL 中 Group by 子句的一部分?

为啥 SQL 强制我在 GROUP BY 子句中重复 SELECT 子句中的所有非聚合字段? [关闭]

SQL 聚合函数一定要跟group by以及NULL的关系的案例精讲