Redshift 中的链式 CTE - 我如何知道 CTE 将继承哪个 DIST KEY?

Posted

技术标签:

【中文标题】Redshift 中的链式 CTE - 我如何知道 CTE 将继承哪个 DIST KEY?【英文标题】:Chained CTEs in Redshift - How do I know which DIST KEY the CTE will inherit? 【发布时间】:2021-05-12 08:20:44 【问题描述】:

我在 Redshift 中有一个视图,它由许多相互连接(链接)的 CTE 组成。在这些 CTE 中,多个表之间存在连接。如果我然后加入一个 CTE,该 CTE 有多个表的连接,连接的 SORT KEY 和 DIST KEY 来自哪里? Redshift如何决定CTE中join的哪个表,CTE应该继承它的DIST KEY和SORT KEY?如果有的话?

例如,tbl1 在 tbl_key 上有一个 DIST KEY,tbl2 在 tbl_id 上有一个 DIST KEY,tbl3 在 tbl_key 上有一个 DIST KEY。

首先,我创建一个 CTE,它是 tbl1 和 tbl2 的连接。

With cte1 as (
    Select tbl1.col1, tbl2.col2
    From tbl1 
    Join tbl2 on tbl1.job_no = tbl2.job_id )

其次,我创建一个连接到第一个 CTE 的 CTE

With cte2 as (
    Select cte1.*, tbl3.col3
    From cte1
    Join tbl3 using (tbl_key))

现在我的问题是,CTE1 在 tbl1 的 tbl_key 的 DIST KEY 或 tbl2 的 tbl_id 的 DIST KEY 上是否有 DIST KEY?或两者?还是两者都没有?

【问题讨论】:

【参考方案1】:

在 Redshift 中,CTE 只是用来简化 sql 的读取。它们的处理方式与子查询相同。即它们不是物理的,因此没有自己的 dist/sort 键。

您可以将代码重写为

Select cte1.*, tbl3.col3
From (Select tbl1.col1, tbl2.col2
      From tbl1
               Join tbl2 on tbl1.job_no = tbl2.job_id
     ) as cte1
         Join tbl3 using (tbl_key)

可以进一步简化为

Select tbl1.col1, tbl2.col2, tbl3.col3
from tbl1
join tbl2 on tbl1.job_no = tbl2.job_id
join tbl3 using (tbl_key)

如果您能够选择您的 dist/sort 键,那么您应该考虑哪些表是最大的并相应地优先考虑这些表。 例如,如果 tbl1 和 tbl2 很大,那么按照您的描述让它们分布可能是有意义的。 但是,如果 tbl2 和 tbl3 都很大,则将两者都分布在 tbl_key 上可能是有意义的。

【讨论】:

【参考方案2】:

当您发出查询时,Redshift 将编译和优化该查询,以实现最佳性能并在逻辑上等效。您的 CTE 看起来像是编译/优化过程的子查询,并且执行连接的顺序可能与您编写查询的方式无关。

Redshift 根据 ANALYZE 创建/更新的表元数据做出这些优化选择。如果您希望 Redshift 对如何将您的表连接在一起做出明智的选择,您将希望您的表元数据是最新的。查询计划(包括连接顺序和数据分布)是在查询编译时设置的,在执行过程中不是动态确定的。

Redshift 做出的选择之一是查询的中间数据如何分布(您的问题),但请记住,这些中间结果可以用于修改后的连接顺序。要查看 Redshift 计划加入表的顺序,请查看查询的 EXPLAIN 计划。您加入的表越多,查询越复杂,Redshift 的选择就越多,EXPLAIN 计划按照您指定的顺序加入的可能性就越小。我处理过包含数十个连接和许多嵌套子查询级别的客户查询,EXPLAIN 计划通常与编写的原始查询有很大不同。

因此,Redshift 正在尝试对连接顺序和中间结果分布做出明智的选择。例如,它通常会先将小表连接到大表,并保持大表的分布。但是这里大大小小的都是基于post WHERE子句过滤和Redshift可以根据元数据做出的猜测。连接越远离源表元数据(连接树的深处),Redshift 就越不确定连接的传入和传出数据的样子。

这里的 EXPLAIN 计划可以为您提供有关 Redshift “思考”的提示 - 如果您看到 DIST INNER 连接 Redshift 正在移动一个表(或中间结果集)的数据以匹配另一个表。如果你 DIST BOTH,那么 Redshift 会将这两组数据重新分配到某个新的分布(通常是列上的连接之一)。这样做是为了避免只有 1 个切片包含数据,而其他所有切片都无事可做,因为这将非常低效。

总结一下 Redshift 计划对您的联接做什么,请查看 EXPLAIN 计划。您还可以从解释计划中推断出一些关于中间结果分布的信息,但它没有提供它计划做什么的完整地图。

【讨论】:

Bill - EXPLAIN 充其量是一个冒险的赌注。它从实际发生的事情中忽略了太多信息,以至于它呈现了一个实际上误导引擎盖下发生的事情的图片。我实际上特别建议使用 EXPLAIN。相反,会检查查询的步骤计划,但这当然只能在查询完成后进行。 @MaxGanzII 我同意解释计划没有描绘查询期间发生的事情的完整画面,因此在优化期间使用可能会产生误导。然而,它是执行连接的计划,在执行过程中不会改变,它是一个更简单的获取和解析报告。 是的。但是 - 请注意 - 这不是绘制完整或不完整图片的情况 - 那些连续统一体,从完整到不完整,不是 EXPLAIN 所依赖的连续统一体。 EXPLAIN 呈现出一幅误导的画面,也就是说,一幅与实际发生的情况根本不同的画面。除了缺少的东西之外,这不是正确的情况;它实际上是主动不正确的。

以上是关于Redshift 中的链式 CTE - 我如何知道 CTE 将继承哪个 DIST KEY?的主要内容,如果未能解决你的问题,请参考以下文章

在 Redshift 中连接字符串的递归 CTE 替代方案

为啥不能在 Redshift 的 CTE 的某些子句中调用不可变的 UDF?

在 Redshift 中解析 JSON 6 级深度

Redshift - 如何识别查询中的低性能区域?

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

如何知道 Redshift 查询返回的值的类型?