Amazon Redshift 如何从列式存储中重建一行?

Posted

技术标签:

【中文标题】Amazon Redshift 如何从列式存储中重建一行?【英文标题】:How does Amazon Redshift reconstruct a row from columnar storage? 【发布时间】:2020-09-16 15:20:44 【问题描述】:

Amazon describes columnar storage like this:

所以我猜这意味着在 PostgreSQL 所谓的“堆”中,块包含一列的所有值,然后是下一列,依此类推。

假设我想查询所有 30 多岁的人,并且我想知道他们的姓名。所以列存储意味着只需要更少的 IO 来读取每行的年龄并找到那些 30 多岁的,因为不需要读取所有其他列。也可以应用一些有效的压缩。我猜这很整洁。

然后呢?这个数据结构本身并不能解释在那之后会发生什么有用的事情。在确定了哪些记录是 30 多岁之后,如何找到关联的名称?使用什么数据结构?它的性能特点是什么?

【问题讨论】:

【参考方案1】:

如果 Age 列是 排序键,则表中的行将按 Age 顺序存储。这很棒,因为磁盘上的每个 1MB 存储块仅保留一列的数据,并且会记录块内的最小值和最大值

因此,搜索包含 30 岁的行意味着 Redshift 可以“跳过”不包含 Age=30 的块。由于从磁盘读取是数据库中最慢的部分,这意味着它可以运行得更快。

一旦找到可能包含Age=30的块,它就会从磁盘读取这些块。块被压缩,因此它们可能包含比磁盘上的 1MB 更多的数据。这意味着可以通过更少的磁盘访问来读取许多行。

一旦这些块被解压缩到内存中,它会找到带有Age=30 的行,然后为Name 列加载相应的块。 Name 列的压缩率会有所不同,因为它是文本且未排序,因此这可能会导致从磁盘为Name 加载比Age 更多的块。

Redshift 然后为所需行组合来自NameAge 的数据并执行任何剩余操作。

这些操作也根据分布键在多个节点上并行,它根据给定的列分布数据(或在节点之间复制数据以用于常用表)。数据通常基于JOIN 语句中经常使用的列进行分布,以便相似的数据位于同一节点上。每个节点将自己的数据返回给Leader Node,Leader节点将数据组合起来,提供最终的结果。

底线:尽量减少从磁盘读取的数据量并在不同节点上并行化操作。

【讨论】:

我相信关键部分有它会找到 Age=30 的行,然后为 Name 列加载相应的块。 后续问题:Finding rows must be getting ctid 的集合(或 redshift 中的等价物)。我相信此信息必须包含每列的块位置,以获取 Name 列或其他列的值(如果在查询中选择)。是这样吗? 我不知道细节,但我想每个块都包含一个“起始行号”,因此 Redshift 可以识别哪个块包含哪些行。这将因列而异,因为某些列包含的信息较少并且压缩方式不同。有一些关于不压缩排序键列的警告,因为它可能导致必须为其他列检索太多相应的块。有各种关于它的文章,例如:Why You Should Not Compress RedShift Sort Key Column “Redshift 然后将 Name 和 Age 中的数据组合到所需的行”,是的,但是如何?感谢您花时间回答,但我真的在寻找这一部分的详细信息。 这将是领导节点在结合每个计算节点返回的响应时所做的神秘事情。如果您正在寻找有关 Redshift 如何运作的“更深入”的信息,我建议您查看:AWS re:Invent 2018: Deep Dive and Best Practices for Amazon Redshift - YouTube 不,我不认为它是领导节点。一行的所有数据都在某个计算节点上,因此除了只涉及一列的琐碎查询之外,计算节点必须以某种方式找到相关行的其他列的数据,然后才能将任何内容返回到领导节点。【参考方案2】:

AFAIK 列式存储中的每个值都有一个 ID 指针(类似于您提到的 CTID),为了获得选择结果 Redshift 需要为从原始数据中选择的每一列查找并组合具有相同 ID 指针的值.如果内存允许它存储在内存中,除非它溢出到磁盘。这个过程称为物化(不要与物化视图物化混淆)。在您的情况下,有两种技术上可能的情况:

    具体化所有年龄/姓名对,然后按 Age=30 过滤,并输出结果 按 Age=30 过滤 Age 列,获取 ID,获取具有相应 ID 的 Name 值,具体化对并输出

我猜在这种情况下会发生 #2,因为物化比过滤更昂贵。但是,有很多场景不太明显(使用复杂的查询和聚合)。查询优化器有责任决定什么更好。 #1 仍然比面向行的要好,因为它仍然只能读取 2 列。

【讨论】:

如何“获取具有相应 ID 的名称值”?它是否扫描整个名称列,寻找它们?它们是否按排序顺序进行,以便可以进行二等分?有btree索引吗?问题是:“使用什么数据结构?它的性能特点是什么?” 我不是 100% 确定,但我认为它看起来像下面这样:每一列都单独存储,分解成块(块)。每个块都有存储该列值范围和行 OID 范围的元数据。因此,如果查询引擎需要根据查询过滤器(如 Age 列)定义 OID,它能够找到并扫描满足条件的块并跳过其余部分,当它尝试实现过滤的名称结果时,OID 也会发生同样的情况按年龄 OID。

以上是关于Amazon Redshift 如何从列式存储中重建一行?的主要内容,如果未能解决你的问题,请参考以下文章

AWS Redshift 列式存储与分布方式

没有函数或存储过程的 Amazon RedShift 中的 Upsert

如何更改 Amazon Redshift 中的默认时区?

amazon redshift 中存储的特定行的默认值在哪里?

如何在 Amazon Redshift 中定期插入数据?

Amazon Redshift 是不是有自己的存储后端