为啥 Redshift 需要进行全表扫描才能找到 DIST/SORT 键的最大值?

Posted

技术标签:

【中文标题】为啥 Redshift 需要进行全表扫描才能找到 DIST/SORT 键的最大值?【英文标题】:Why does Redshift need to do a full table scan to find the max value of the DIST/SORT key?为什么 Redshift 需要进行全表扫描才能找到 DIST/SORT 键的最大值? 【发布时间】:2016-08-23 01:23:22 【问题描述】:

我正在对 Redshift 进行简单测试,以尝试加快将数据插入 Redshift 表的速度。我今天注意到的一件事是做这样的事情

CREATE TABLE a (x int) DISTSTYLE key DISTKEY (x) SORTKEY (x);
INSERT INTO a (x) VALUES (1), (2), (3), (4);
VACUUM a; ANALYZE a;

EXPLAIN SELECT MAX(x) FROM a;

产量

QUERY PLAN
XN Aggregate  (cost=0.05..0.05 rows=1 width=4)
  ->  XN Seq Scan on a  (cost=0.00..0.04 rows=4 width=4)

我知道这只有 4 行,但它仍然不应该进行全表扫描来查找预排序列的最大值。 ANALYZE所做的工作中不包含元数据吗?

作为健全性检查,SELECT x FROM a WHERE x > 3EXPLAIN 仅扫描 2 行而不是整个表。

编辑:我在表中插入了 1,000,000 多行,随机值从 1 到 10,000。抽真空并分析。查询计划仍然说它必须扫描所有 1,000,004 行。

【问题讨论】:

因为只有 4 行任何其他执行计划都没有意义 在任何其他数据库中,可能还有红移,全扫描是 4 行的最佳选择。当你有大约 50,000 行时,你可能会发现它会做其他事情 已编辑。查询计划仍然对 1M 行的表进行全表扫描。 【参考方案1】:

分析小型数据集中的查询计划并不能对数据库如何执行查询产生任何实际的见解。

优化器具有阈值,当不同计划之间的成本差异足够小时,它会停止考虑替代计划。这个想法是,对于简单的查询,搜索“完美”执行计划所花费的时间可能会超过一个不太理想的计划的总执行时间。

Redshift 是在 ParAccel DB 的代码上开发的。 ParAccel 实际上有数百个参数可以更改/调整,以针对不同的工作负载/情况优化数据库。

由于 Redshift 是一种“托管”产品,因此它会将这些设置预设为亚马逊工程师在“预期”工作负载下认为最佳的水平。

一般来说,Redshift 和 ParAccel 对于单片查询来说不是很好。无论如何,这些查询往往会在所有切片中运行,即使它们只会在单个切片中查找数据。

一旦在切片中执行查询,读取的最小数据量就是一个块。根据块大小,这可能意味着数十万行。

请记住,Redshift 没有索引。所以你不会有一个简单的记录查找,它会从索引中读取一些条目,然后激光聚焦在磁盘上的单个页面上。它总是会至少读取该表的整个块,并且会在每个切片中执行此操作。


如何拥有一个有意义的数据集来评估查询计划?

简短的回答是,您的表的每个切片会有“大量”数据块。

我的表需要每个切片有多少块?答案取决于几个因素:

    集群中的节点数 集群中的节点类型 - 每个节点的切片数 数据类型 - 每个值需要多少字节。 涉及的列的压缩编码类型 询问。最佳编码取决于数据人口统计

让我们从顶部开始。

Redshift is an MPP Database, where processing is spread accross multiple nodes. See Redshift's architecture here.

Each node is further sub-divided in slices, which are dedicated data partitions and corresponding hardware resources to process queries on that partition of the data.

当在 Redshift 中创建表并插入数据时,Redshift 将至少为每个切片分配一个块。


这是一个简单的例子:

如果您创建了一个包含两个 ds1.8xlarge 节点的集群,则每个节点将有 16 个切片乘以两个节点,总共 32 个切片。

假设我们正在查询,WHERE 子句中的列类似于“ITEM_COUNT”一个整数。一个整数占用 4 个字节。

Redshift uses a block size of 1MB.

因此,在这种情况下,您的 ITEM_COUNT 列至少可以使用 32 个块乘以 1MB 的块大小,这相当于 32MB 的存储空间。

如果您有 32MB 的存储空间并且每个条目仅消耗 4 个字节,那么您可以拥有超过 800 万个条目,并且它们都可以放在一个块中。

In this example in the Amazon Redshift documentation they load close to 40 million rows to evaluate and compare different encoding techniques. Read it here.


但是等等.....

存在压缩,如果您有 75% 的压缩率,这意味着即使是 3200 万条记录仍然可以放入单个块中。

底线是什么?

为了分析您的查询计划,您需要具有多个块的表、列。在我们上面的示例中,3200 万行仍然是一个块。

这意味着在上面的配置中,在所有假设下,具有单个记录的表基本上很可能与具有 3200 万条记录的表具有相同的查询计划,因为在这两种情况下,数据库只需要读取每片一个块。


如果您想了解数据如何跨切片分布以及使用了多少块,您可以使用以下查询:

每片有多少行:

Select trim(name) as table_name, id, slice, sorted_rows, rows
from stv_tbl_perm
where name like '<<your-tablename>>'
order by slice;

如何计算多少块:

select trim(name) as table_name, col,  b.slice, b.num_values, count(b.slice)
from stv_tbl_perm a, stv_blocklist b
where a.id = b.tbl
  and a.slice = b.slice
and name like '<<your-tablename>>'
group by 1,2,3,4
order by col, slice;

【讨论】:

第 4 段 - 我认为您的意思是最佳的,而不是可选的,但很高兴得到纠正。 我在具有 100 万行的表上获得了相同的查询计划(对所有 100 万行进行顺序扫描)。您是说在执行时,查询实际上不会扫描所有行吗?有没有办法深入了解实际的查询执行情况? 一百万行可能仍然很小。我知道当你来自另一个数据库平台时这很疯狂,但在 Redshift 中,规模会发生变化。我不知道您的集群的大小或数据的人口统计数据,但我添加了一个很长的解释,说明 Redshift 如何能够在每个切片的单个块中存储大量数据。您可以使用我提供的查询来查看您的特定表的每个切片有多少块。 感谢您提供更全面的回答。这绝对帮助我了解发生了什么。 不客气。稍后我将使用节点、切片和块的图片来更新答案。谢谢!

以上是关于为啥 Redshift 需要进行全表扫描才能找到 DIST/SORT 键的最大值?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 oracle 表索引但仍然进行全表扫描?

即使使用 where 子句中使用的排序键,Redshift 也会执行全表扫描

为啥 Oracle 在应该使用索引时使用全表扫描?

避免全表扫描

mysql select * order by 索引 limit0,10 为啥是全表扫描

MySQL(版本:5.7.21-ndb-7.5.9-cluster)使用全表扫描进行简单的选择查询