防止 Amazon S3 节流
节流指限制服务、应用程序或系统使用速率的过程。在 Amazon 中,您可以使用节流防止过度使用 Amazon S3 服务,并提高 Amazon S3 对所有用户的可用性和响应能力。但是,由于节流会限制在 Amazon S3 中传入或传出数据的速率,因此必须考虑防止您的交互受到节流。
减少服务级别节流
为了避免服务级别下的 Amazon S3 节流,您可以监控使用情况并调整服务限额,也可以使用分区等特定方法。下面是一些可能导致节流的一些情况:
-
超过账户的 API 请求限制 - Amazon S3 具有基于账户类型和使用情况的默认 API 请求限制。如果超过单个对象每秒的最大请求数,则可能会对您的请求进行节流,以防 Amazon S3 服务过载。
-
数据分区不足 - 如果您没有正确分区数据并传输大量数据,Amazon S3 可能会对您的请求进行节流。有关分区的更多信息,请参阅本文档中的 使用分区 节。
-
大量小对象 - 如有可能,避免生成大量小文件。Amazon S3 的每个分区前缀每秒最多 5500 个 GET 请求,您的 Athena 查询也具有同样的限制。如果您需要在单一查询中扫描数百万个小对象,则 Amazon S3 可能会对您的查询进行节流。
要避免过度扫描,可使用 Amazon Glue ETL 定期压缩文件,或者对表进行分区并添加分区键筛选条件。有关详细信息,请参阅以下资源:
-
如何配置 Amazon Glue ETL 作业以输出较大的文件?
(Amazon 知识中心) -
以较大的组读取输入文件(《Amazon Glue 开发人员指南》)
优化表
如果您遇到节流问题,则需要对数据进行结构化。尽管 Amazon S3 可以处理大量数据,但由于数据的结构方式,有时也会发生节流。
以下各节就如何在 Amazon S3 中结构化数据以免出现节流问题提供了一些建议。
使用分区
您可以使用分区通过限制在任何给定时间必须访问的数据量来减少节流。通过对特定列中的数据进行分区,您可以将请求均匀地分配到多个对象,并减少单一对象的请求数量。可通过减少扫描的数据量提高查询性能并降低成本。
创建表时,您可以定义分区,来充当虚拟列。要在 CREATE TABLE
语句中创建包含分区的表,可使用 PARTITIONED BY (
子句定义用于对数据进行分区的键。column_name
data_type
)
要限制查询扫描的分区,可以在查询的 WHERE
子句中将其指定为谓词。因此,经常用作筛选条件的列是用于分区的理想候选项。常见做法是根据时间间隔对数据进行分区,这可能会导致多层分区方案。
请注意,分区也有成本。当您增加表中的分区数时,检索和处理分区元数据所需的时间也会增加。因此,过度分区可能会抵消以更明智的方式进行分区带来的好处。如果您的数据严重偏向于一个分区值,并且大多数查询都使用该值,则可能会产生额外开销。
有关在 Athena 中进行分区的更多信息,请参阅 什么是分区?。
对数据进行分桶
对数据进行分区的另一种方法是对单一分区中的数据进行分桶。进行分桶时,可指定包含要分组到一起的行的一列或多列。然后,将这些行放入多个存储桶中。这样,只需查询必须读取的存储桶即可,从而减少必须扫描的数据行数。
在选择要用于分桶的列时,选择具有高基数(即具有许多不同值)、分布均匀且经常用于筛选数据的列。例如,ID 列等主键就是用于分桶的理想列。
有关在 Athena 中分桶的更多信息,请参阅 什么是分桶?。
使用 Amazon Glue 分区索引
您可以使用 Amazon Glue 分区索引根据一个或多个分区值对表中的数据进行组织。Amazon Glue 分区索引可以减少数据传输次数、数据处理量和查询处理时间。
Amazon Glue 分区索引是一个元数据文件,其中包含有关表中分区的信息,包括分区键及其值。分区索引存储在 Amazon S3 存储桶中,并在向表中添加新分区时由 Amazon Glue 自动更新。
当存在 Amazon Glue 分区索引时,查询会尝试获取一部分的分区,而不是加载表中的所有分区。将仅对与查询有关的这部分数据运行查询。
在 Amazon Glue 中创建表时,可以基于表中定义的任意分区键组合创建分区索引。在表中创建了一个或多个分区索引后,必须向表中添加用于启用分区筛选的属性。然后,您可以从 Athena 查询表。
有关在 Amazon Glue 中创建分区索引的信息,请参阅《Amazon Glue 开发人员指南》中的在 Amazon Glue 中使用分区索引。有关添加表属性以启用分区筛选的信息,请参阅 Amazon Glue 分区索引和筛选。
使用数据压缩和文件拆分
如果文件大小理想或者可以按逻辑组对其进行拆分,则数据压缩可以显著加快查询速度。通常,较高的压缩比需要更多的 CPU 周期才能压缩和解压缩数据。对于 Athena,建议使用 Apache Parquet 或 Apache ORC,以默认压缩数据。有关 Athena 中数据压缩的信息,请参阅 Athena 压缩支持。
可通过拆分文件允许 Athena 将读取单个文件的任务分配给多个读取器,从而提高并行度。如果单个文件不可拆分,则只有一个读取器可以读取该文件,而其他读取器处于空闲状态。Apache Parquet 和 Apache ORC 也支持可拆分文件。
使用经过优化的列式数据存储
如果将数据转换为列式格式,则会显著提高 Athena 查询性能。生成列式文件时,要考虑的一种优化方法是根据分区键对数据进行排序。
Apache Parquet 和 Apache ORC 是常用的开源列式数据存储。有关将现有 Amazon S3 数据来源转换为其中一种格式的信息,请参阅 转换为列式格式。
使用较大的 Parquet 块大小或 ORC 条带大小
Parquet 和 ORC 具有诸多数据存储参数,您可以调整这些参数以进行优化。在 Parquet 中,您可以针对块大小进行优化。在 ORC 中,您可以针对条带大小进行优化。数据块或条带越大,可在其中存储的行越多。默认情况下,Parquet 块大小为 128MB,ORC 条带大小为 64MB。
如果 ORC 条带小于 8MB(hive.orc.max_buffer_size
的默认值),Athena 会读取整个 ORC 条带。这是 Athena 在列选择性和针对较小条带的每秒输入 / 输出操作之间做出的权衡。
如果表的列数非常多,则较小的数据块或条带大小可能会导致扫描的数据量超出必要范围。在此类情况下,较大的块大小可能更高效。
使用 ORC 处理复杂类型
目前,当您查询在 Parquet 中存储的具有复杂数据类型(例如,array
、map
或 struct
)的列时,Athena 将读取整行数据,而不是有选择性地仅读取指定列。这是 Athena 中的已知问题。要解决该问题,考虑使用 ORC。
选择压缩算法
可配置的另一个参数是数据块的压缩算法。有关 Athena 中 Parquet 和 ORC 支持的压缩算法的信息,请参阅 Athena 压缩支持。
有关优化 Athena 列式存储格式的更多信息,请参阅 Amazon 大数据博客文章 Amazon Athena 的十大性能调整技巧
使用 Iceberg 表
Apache Iceberg 是一种适用于超大型分析数据集的开放表格式,专为优化 Amazon S3 使用而设计。可使用 Iceberg 表帮助减少 Amazon S3 节流。
Iceberg 表具有以下优势:
-
可基于一列或多列对 Iceberg 表进行分区。这将优化数据访问并减少查询必须扫描的数据量。
-
由于 Iceberg 对象存储模式可优化 Iceberg 表以与 Amazon S3 配合使用,因此它可以处理大量数据和繁重的查询工作负载。
-
对象存储模式下的 Iceberg 表具有可扩展性、容错性和耐用性,这有助于减少节流。
-
ACID 事务支持意味着多个用户可以原子方式添加和删除 Amazon S3 对象。
有关 Apache Iceberg 的更多信息,请参阅 Apache Iceberg
优化查询
使用本节中的建议优化 Athena SQL 查询。
在 ORDER BY 子句中使用 LIMIT
ORDER BY
子句按排序顺序返回数据。这要求 Athena 将所有数据行发送到单一 Worker 节点,然后对这些行进行排序。此类查询可能会运行很长时间,甚至会失败。
为了提高查询效率,查看前或后 N
个值,然后还要使用 LIMIT
子句。这将显著降低排序成本,因为会将排序和限制推送到各个 Worker 节点而不是单一 Worker。
优化 JOIN 子句
当您联接两个表时,Athena 会将右侧的表分配给 Worker 节点,然后流式处理左侧的表以执行联接。
因此,在联接的左侧指定较大的表,在联接的右侧指定较小的表。这样可减少 Athena 所用的内存并降低查询运行延迟。
同时注意以下几点:
-
使用多个
JOIN
命令时,按从大到小顺序指定表。 -
除非查询需要,否则避免使用交叉联接。
优化 GROUP BY 子句
GROUP BY
运算符根据 GROUP
BY
列将行分配给 Worker 节点。这些列将在内存中引用,并在载入行时对值进行比较。当 GROUP BY
列匹配时,这些值会聚合在一起。考虑到此过程的工作方式,建议按基数从高到低对列进行排序。
使用数字代替字符串
由于与字符串相比,数字所需的内存更少且处理速度更快,因此尽可能使用数字代替字符串。
限制列数
要减少存储数据所需的总内存量,限制在 SELECT
语句中指定的列数。
使用正则表达式代替 LIKE
在大型字符串中包含 LIKE '%string%'
等子句的查询的计算量可能非常大。在字符串列中筛选多个值时,改用 regexp_like()
使用 LIMIT 子句
在运行查询时,使用 LIMIT
子句仅返回所需的列,而不是选择所有列。这减小了通过查询执行管道处理的数据集的大小。当查询包含大量基于字符串的列的表时,建议使用 LIMIT
子句。当您对任何查询执行多个联接或聚合时,也建议使用 LIMIT
子句。
另请参阅
《Amazon Simple Storage Service 用户指南》中的最佳实践设计模式:优化 Amazon S3 性能。