如何排除 Amazon Redshift 占用大量磁盘空间或用尽磁盘空间的问题?

上次更新时间:2020 年 3 月 27 日

我遇到了 Amazon Redshift 占用大量磁盘空间或用尽磁盘空间的问题。我如何排除占用大量磁盘空间的问题?

解决方法

占用大量磁盘空间错误可能取决于以下多种因素:

  • 分配和排序键
  • 查询处理
  • 带有 VARCHAR(MAX) 列的表
  • 高列压缩
  • 维护操作
  • 具有交叉联接的笛卡尔积
  • 最小表大小
  • 逻辑删除数据块
  • 复制大型文件

分配和排序键

查看表的分配样式、分配键和排序键选择。磁盘节点空间用尽可能是由分配偏斜的表导致的,其中一个节点中的数据比其他节点多。如果表的分配样式为偏斜分配样式,则将分配样式更改为更统一的分配方式。请注意,分配和行偏斜可能会影响查询执行期间的存储偏斜和中间行集。有关分配键和排序键的详细信息,请参见 Amazon Redshift Engineering’s Advanced Table Design Playbook: Preamble, Prerequisites, and Prioritization

要确定分配键的基数,请运行以下查询:

select distkey, count(*) from public.distribution_skew group by distkey having count(*) > 1 order by 2 desc;

注意:要避免排序步骤,请在 ORDER BY 子句中使用 SORT KEY 列。排序步骤可能会占用过多的内存,从而导致磁盘满溢。有关更多信息,请参阅选择排序键

要查看分配键中的数据库块如何映射到集群,请使用 Amazon Redshift table_inspector.sql 实用程序

查询处理

查看分配给查询的所有内存。在处理查询时,中间查询结果可能会存储在临时数据块中。如果没有足够的可用内存,则表会导致磁盘满溢。中间结果集未压缩,这会影响可用的磁盘空间。有关更多信息,请参见分配给查询的内存不足

Amazon Redshift 默认为均匀分配的表结构,并且没有临时表的列编码。但是,如果您使用的是 SELECT...INTO 语法,请使用 CREATE 语句。有关更多信息,请参见 Top 10 Performance Tuning Techniques for Amazon Redshift,并按照 Tip #6: Address the inefficient use of temporary tables(提示 6:解决临时表使用效率低下的问题)中的说明操作。

如果为查询分配的内存不足,您可能会在 SVL_QUERY_SUMMARY 中看到一个步骤,该步骤中 is_diskbased 的值为“true”。要解决此问题,请增加查询插槽的数量,以便为查询分配更多内存。有关如何临时增加查询插槽的详细信息,请参见 wlm_query_slot_count调整 WLM 以运行混合工作负载。您还可以使用 WLM 查询监控规则来应对繁重的处理负载问题并识别 I/O 密集型查询。

带有 VARCHAR(MAX) 列的表

检查 VARCHAR 或 CHARACTER VARYING 列中是否有在数据存储在磁盘上时可能忽略的尾部空格。在查询处理过程中,尾部空格可占用内存中的全长(VARCHAR 的最大值为 65535)。最佳实践是使用尽可能小的列

要生成具有最大列宽的表格列表,请运行以下查询:

SELECT database, schema || '.' || "table" AS "table", max_varchar FROM svv_table_info WHERE max_varchar > 150 ORDER BY 2;

要标识和显示宽 VARCHAR 表列的实际宽度,请运行以下查询:

SELECT max(octet_length (rtrim(column_name))) FROM table_name;

有关表格设计的更多信息,请查看设计表格的 Amazon Redshift 最佳实践

高列压缩

使用 ANALYZE COMPRESSIONAmazon Redshift 列编码实用程序,以获得最佳列编码。Amazon Redshift 提供了列编码,这可提高读取性能,同时降低整体存储消耗。最佳实践是使用系统压缩功能。

维护操作

请确保定期分析和清理 Amazon Redshift 数据库中的数据库表。维持数据库正常运行的一种方法是识别任何丢失或过时的统计信息。这可防止 Amazon Redshift 扫描任何不必要的表格行,还有助于优化查询执行计划。

注意:VACUUM 和 DEEP COPY 等维护操作使用临时存储空间进行分类操作,因此预计会出现磁盘使用量激增的情况。

例如,以下查询可帮助您识别 Amazon Redshift 中的过时统计信息:

SELECT * FROM svv_table_info WHERE stats_off > 10 ORDER BY size DESC;

有关更多信息,请参见 Amazon Redshift 分析和清理架构实用程序

具有交叉联接的笛卡尔积

使用查询的 EXPLAIN 执行计划查找笛卡尔积相关的查询。笛卡尔产品具有不相关的交叉联接,可能会产生更多数据块。这些交叉联接可以提高内存利用率,并且会有更多的表溢出到磁盘。如果交叉联接不共享 JOIN 条件,则联接将生成两个表的笛卡尔积。然后,一个表的每一行都会联接到另一个表的每一行。

交叉联接还可以作为嵌套循环联接执行,这需要最长的处理时间。嵌套循环联接会导致整体磁盘使用量出现峰值。有关更多信息,请参见使用嵌套循环识别查询

最小表大小

同一个表在不同的集群中可能会具有不同的大小。然后,最小表大小由列数以及表中是否有 SORTKEY 及其切片数决定。如果您最近调整了 Amazon Redshift 集群的大小,您可能会看到整体磁盘存储空间发生了变化。这是由切片数量发生变化引起的。Amazon Redshift 还会对每个表所使用的表段进行计数。有关更多信息,请参阅为什么 Amazon Redshift 集群中的表使用的磁盘存储空间高于或低于预期?

逻辑删除数据块

当向 Amazon Redshift 表进行 WRITE 事务且存在并发读取时,将生成逻辑删除数据块。Amazon Redshift 在写入操作之前保留数据块,以保持并发读取操作的一致性。无法更改 Amazon Redshift 数据块。每个插入更新删除操作都会创建一组新的数据块,将旧数据块标记为逻辑删除。

有时,由于表交易的运行时间过长,逻辑删除在提交阶段无法清除。当同时运行的 ETL 负载太多时,逻辑删除也可能无法清除。由于 Amazon Redshift 从交易开始时就会监视数据库,因此写入数据库的任何表也会保留逻辑删除数据块。如果长期运行的表事务定期发生并跨多个负载,则会累积足够多的逻辑删除而导致磁盘已满错误。

您还可以通过执行提交命令强制 Amazon Redshift 执行关于逻辑删除数据块的分析。

如果有长期运行的查询处于活动状态,则使用提交命令终止查询(并释放所有后续数据块):

begin;
create table a (id int);
insert into a values(1);
commit;
drop table a; 

要确认逻辑删除数据块,请运行以下查询:

select trim(name) as tablename, count(case when tombstone > 0 then 1 else null end) as tombstones from svv_diskusage group by 1 having count(case when tombstone > 0 then 1 else null end) > 0 order by 2 desc;

复制大型文件

在 COPY 操作期间,即使有足够的可用存储空间,您也可能会收到磁盘已满错误。如果排序操作溢出到磁盘并创建临时数据块,则会发生此错误。

如果遇到磁盘已满错误信息,请检查 STL_DISK_FULL_DIAG 表:

select '2000-01-01'::timestamp + (currenttime/1000000.0)* interval '1 second' as currenttime,node_num,query_id,temp_blocks from pg_catalog.stl_disk_full_diag;

有关更多最佳实践,请参见加载数据的 Amazon Redshift 最佳实践

其他问题排查方法

检查 Amazon Redshift 控制台性能选项卡下方的磁盘空间百分比。对于每个集群节点,Amazon Redshift 会提供额外的磁盘空间,该空间大小大于标称磁盘容量。

如果您注意到利用率突然激增,请使用 STL_QUERY 以确定正在运行的活动和作业:

select * from stl_query where starttime between '2018-01-01 00:30:00' and '2018-01-01 00:40:00';

注意:使用发生峰值的时间更新值。

要确定前 20 个磁盘溢出查询,请运行以下查询:

select A.userid, A.query, blocks_to_disk, trim(B.querytxt) text from stl_query_metrics A, stl_query B where A.query = B.query and segment=-1 and step = -1 and max_blocks_to_disk > 0 order by 3 desc limit 20;

要确定您的查询是否正确写入磁盘,请运行以下查询:

SELECT q.query, trim(q.cat_text)
FROM (
SELECT query,
replace( listagg(text,' ') WITHIN GROUP (ORDER BY sequence), '\\n', ' ') AS cat_text
FROM stl_querytext
WHERE userid>1
GROUP BY query) q
JOIN (
SELECT distinct query
FROM svl_query_summary
WHERE is_diskbased='t' AND (LABEL ILIKE 'hash%' OR LABEL ILIKE 'sort%' OR LABEL ILIKE 'aggr%' OR LABEL ILIKE 'save%' OR LABEL ILIKE 'window%' OR LABEL ILIKE 'unique%')
AND userid > 1) qs
ON qs.query = q.query;

这篇文章对您有帮助吗?

我们可以改进什么?


需要更多帮助?