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 然后为所需行组合来自Name
和Age
的数据并执行任何剩余操作。
这些操作也根据分布键在多个节点上并行,它根据给定的列分布数据(或在节点之间复制数据以用于常用表)。数据通常基于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 如何从列式存储中重建一行?的主要内容,如果未能解决你的问题,请参考以下文章
没有函数或存储过程的 Amazon RedShift 中的 Upsert