admin

为什么Redshift需要进行全表扫描以找到DIST / SORT键的最大值?

sql

我正在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;

yields

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

就像进行完整性检查一样,EXPLAINforSELECT x FROM a WHERE x > 3只扫描2行而不是整个表。

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


阅读 175

收藏
2021-06-07

共1个答案

admin

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

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

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

由于Redshift是“托管”产品,因此在“预期”工作量的前提下,将这些设置预设为Amazon工程师认为最佳的水平。

通常,Redshift和ParAccel对于单切片查询而言并不是那么好。这些查询无论如何都倾向于在所有片中运行,即使它们只是在单个片中查找数据也是如此。

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

请记住,Redshift没有索引。因此,您将不会有一个简单的记录查找,该查找将从索引中读取一些条目,然后将注意力集中在磁盘上的单个页面上。它将始终至少读取该表的整个块,并将在每个片中进行读取。


如何拥有有意义的数据集以能够评估查询计划?

简短的答案是,您的表中每个切片将具有“大量”的数据块。

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

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

因此,让我们从顶部开始。

Redshift是一个MPP数据库,其中处理分布在多个节点上。在这里查看Redshift的体系结构。

每个节点进一步细分为多个切片,这些切片是专用数据分区和相应的硬件资源,用于处理对该数据分区的查询。

在Redshift中创建表并插入数据后,Redshift将为每个片分配最少一个块。


这是一个简单的示例:

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

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

Redshift使用的块大小为1MB。

因此,在这种情况下,您的ITEM_COUNT列将至少具有32个块乘以1MB的块大小,这等于32MB的存储量。

如果您拥有32MB的存储空间,并且每个条目仅占用4个字节,则您可以拥有超过800万个条目,并且它们都可以容纳在单个块中。

在Amazon
Redshift文档的此示例中,它们加载了将近4000万行,以评估和比较不同的编码技术。在这里阅读。


可是等等.....

如果有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;
2021-06-07