多连接查询上的 Redshift 磁盘已满

Posted

技术标签:

【中文标题】多连接查询上的 Redshift 磁盘已满【英文标题】:Redshift Disk Full on Multiple Join Query 【发布时间】:2017-10-24 02:02:15 【问题描述】:

我有两张桌子。第一个我们称之为“main”,它包括六个列(a-f),它们在另一个名为“lookup”的表中一对一映射。我的查询如下:

with
a_options as (*a options*),
...,
f_options as (*f options*),
other as (*query on base1*),
main as (select ... from a_options,..., f_options, other),
lookup as (*query on base2*)
select *,
  a_lookup.value,
  b_lookup.value,
  c_lookup.value,
  d_lookup.value,
  e_lookup.value,
  f_lookup.value
from main
inner join lookup as a_lookup on a = a_lookup.key
inner join lookup as b_lookup on b = b_lookup.key
inner join lookup as c_lookup on c = c_lookup.key
inner join lookup as d_lookup on d = d_lookup.key
inner join lookup as e_lookup on e = e_lookup.key
inner join lookup as f_lookup on f = f_lookup.key

我在 16 个 dc1.large 节点的 Redshift 集群上运行它。在整个集群中,我的磁盘空间利用率约为 60%,这意味着我应该有不超过 240 GB 的内存和 1.02 TB 的可用磁盘空间(这是一个很高的估计,因为其中一些是为 Redshift 内部使用而保留的) .

正如我所提到的,这些连接中的每一个都是一对一的,因此查询的结果应该不大于 main 的大小。当 main 为 4,496 行时,查询在大约 15 秒内执行,并且几乎不使用磁盘空间。但是,在 7,304 行(主要以离散增量增长)时,查询在大约 5 分钟后因磁盘已满错误而失败。

CloudWatch 显示错误是由其中一个节点达到存储容量引起的。在整个查询过程中,存储不会在节点之间均匀增长,并且当第一个节点达到 100% 时,查询会准确地失败,因此查询不会消耗集群中的所有可用磁盘空间。尽管如此,它不应该接近容量。有没有人见过这种行为?为什么我的查询会这样爆炸?

数据库几乎完全由新表组成,因此没有任何碎片。我对数据库的设计也没有太多控制权,所以我无法重构我的表以优化性能(我意识到查询中有六个连接可能表明设计不佳)。我只是想了解为什么 Redshift 会占用这么多存储空间。

编辑: main 和 lookup 都是派生表,每个表都在 CTE 中定义。 a-f 有几个不同的选项。 Main 是通过首先计算 a-f(交叉连接)的每个不同组合,然后将其与另一组数据连接来生成的。这另一组数据(92 行)也是一个 CTE,它是另一个表(196,154,352 行,称为 base1)的过滤和聚合版本。对于 a-f 的每种组合,main 中将有大约 30 个不同的行(这就是 main 离散增加大小的原因,这取决于 a-f 有多少选项)。同样,当 main 大约有 7,000 行时,查询开始占用磁盘空间(a-f 的平均 2.5 个选项)。查找只是另一个表的过滤和聚合版本,我将其称为 base2(从 172,867 行减少到 1,241 行)。

所以 base1 和 base2 是这个查询中唯一的真实表。 Main 是从 a-f 与另一个从 base1 派生的 CTE 的交叉联接派生的,而查找是直接从 base2 派生的。请参阅上面的查询更新。

【问题讨论】:

查询的中间步骤占用的空间比仅查看表明显的要多。这是我的猜测。 是的,我也是这么想的。我尝试了一个连接、两个连接、三个连接等的查询,它给我的结果小于所有东西的 main 大小。我还将它分解为显式嵌套查询,但我仍然遇到了同样的错误。我对 Redshift/Postgres 的“幕后”加入操作了解不多,但似乎有些不对劲。 您在连接列上有索引吗?除了可能加快查询速度外,它还可能降低空间复杂度。 @TimBiegeleisen:Redshift 不支持索引:docs.aws.amazon.com/redshift/latest/dg/… 时间、集群大小和表大小感觉完全不对!我在 1 x dc2.large 上运行更大版本的此类东西,顺便说一下,您应该升级到 dc2。 【参考方案1】:

请您尝试一下,一旦我遇到类似的问题并像这样拆分解决了它。

with part1 as 
(
select *,
a_lookup.value,
b_lookup.value,
c_lookup.value
from main
inner join lookup as a_lookup on main.a = a_lookup.key
inner join lookup as b_lookup on main.b = b_lookup.key
inner join lookup as c_lookup on main.c = c_lookup.key
)
select p1.*,
  d_lookup.value,
  e_lookup.value,
  f_lookup.value
from part1 as p1
inner join lookup as d_lookup on p1.d = d_lookup.key
inner join lookup as e_lookup on p1.e = e_lookup.key
inner join lookup as f_lookup on p1.f = f_lookup.key
;

【讨论】:

@joshua-dotson 你试过这个吗?它对你有用吗?我认为您的联接/数据仍然存在问题 - 即使这可行。 不幸的是,这并没有解决问题。我应该提到 main 和 lookup 实际上都是派生表,每个表都在 CTE 中定义。我会将该信息添加到原始问题中,以防万一。 是的,CTE 用于生成“main”和“lookup”确实很重要。在 Redshift 中,CTE 的使用有点像“视图”(不像 postgres,它们在使用时被计算并存储为临时表)。您需要更新您的问题,以包含有关这些初始 CTE 的信息,以及源表的大小和定义。 我不知道它们在 Redshift 中被视为视图,这非常有帮助。我在我的问题中添加了一些信息。您认为这可能是问题所在吗?

以上是关于多连接查询上的 Redshift 磁盘已满的主要内容,如果未能解决你的问题,请参考以下文章

我可以在 Redshift 上的存储过程中将两个查询连接在一起吗?

使用 Django 在 Heroku 上的 Redshift 连接

临时表上的 distkey 和 sortkey - Redshift

在 Redshift 中查找常用连接查询

如何在没有连接的情况下为 postgres (Redshift) 生成 SQL 查询?

postgresql数据库连接数查询