如何排除 Amazon Redshift 占用大量磁盘空间或用尽磁盘空间的问题?
上次更新日期:2021 年 2 月 22 日
我遇到了 Amazon Redshift 占用大量磁盘空间或用尽磁盘空间的问题。我如何排除占用大量磁盘空间的问题?
解决方法
占用大量磁盘空间错误可能取决于以下多种因素:
- 分配和排序键
- 查询处理
- 带有 VARCHAR(MAX) 列的表
- 高列压缩
- 维护操作
- 具有交叉联接的笛卡尔积
- 最小表大小
- 逻辑删除数据块
- 复制大型文件
分配和排序键
查看表的分配样式、分配键和排序键选择。磁盘节点空间用尽可能是由分配偏斜的表导致的,其中一个节点中的数据比其他节点多。如果表的分配样式为偏斜分配样式,则将分配样式更改为更统一的分配方式。请注意,分配和行偏斜可能会影响查询运行时的存储偏斜和中间行集。有关分配键和排序键的详细信息,请参阅 Amazon Redshift engineering’s advanced table design playbook: preamble, prerequisites, and prioritization。
要确定分配键的基数,请运行以下查询:
SELECT <distkey column>, COUNT(*) FROM <schema name>.<table with distribution skew> GROUP BY <distkey column> HAVING COUNT(*) > 1 ORDER BY 2 DESC;
注意:要避免排序步骤,请在 ORDER BY 子句中使用 SORT KEY 列。排序步骤可能会占用过多的内存,从而导致磁盘满溢。有关更多信息,请参阅选择排序键。
要查看分配键中的数据库块如何映射到集群,请使用 Amazon Redshift table_inspector.sql 实用程序。
查询处理
查看分配给查询的所有内存。在处理查询时,中间查询结果可以存储在临时块中。如果没有足够的可用内存,则表会导致磁盘满溢。中间结果集未压缩,这会影响可用的磁盘空间。有关更多信息,请参阅分配给查询的内存不足。
Amazon Redshift 默认为均匀分配的表结构,并且没有临时表的列编码。但是,如果您使用的是 SELECT...INTO 语法,请使用 CREATE 语句。有关更多信息,请参阅 Amazon Redshift 的十大性能调整技术,并按照提示 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 COMPRESSION 或 Amazon 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 控制台中 Performance(性能)选项卡下方的磁盘空间百分比。对于每个集群节点,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;