

# 防止 Amazon S3 节流
<a name="performance-tuning-s3-throttling"></a>

节流指限制服务、应用程序或系统使用速率的过程。在 AWS 中，您可以使用节流防止过度使用 Amazon S3 服务，并提高 Amazon S3 对所有用户的可用性和响应能力。但是，由于节流会限制在 Amazon S3 中传入或传出数据的速率，因此必须考虑防止您的交互受到节流。

正如[性能优化](performance-tuning.md)一章所指出的那样，优化可能取决于您的服务级别决策、如何构建表和数据的结构以及如何编写查询。

**Topics**
+ [减少服务级别节流](performance-tuning-s3-throttling-reduce-throttling-at-the-service-level.md)
+ [优化您的表](performance-tuning-s3-throttling-optimizing-your-tables.md)
+ [优化您的查询](performance-tuning-s3-throttling-optimizing-queries.md)

# 减少服务级别节流
<a name="performance-tuning-s3-throttling-reduce-throttling-at-the-service-level"></a>

为了避免服务级别下的 Amazon S3 节流，您可以监控使用情况并调整[服务限额](https://docs.aws.amazon.com/general/latest/gr/s3.html#limits_s3)，也可以使用分区等特定方法。下面是一些可能导致节流的一些情况：
+ **超过账户的 API 请求限制** - Amazon S3 具有基于账户类型和使用情况的默认 API 请求限制。如果超过单个前缀每秒的最大请求数，则可能会对您的请求进行节流，以防 Amazon S3 服务过载。
+ **数据分区不足** - 如果您没有正确分区数据并传输大量数据，Amazon S3 可能会对您的请求进行节流。有关分区的更多信息，请参阅本文档中的 [使用分区](performance-tuning-s3-throttling-optimizing-your-tables.md#performance-tuning-s3-throttling-use-partitioning) 节。
+ **大量小对象** - 如有可能，避免生成大量小文件。Amazon S3 的每个分区前缀每秒最多 [5500 个 GET 请求](https://docs.aws.amazon.com/AmazonS3/latest/userguide/optimizing-performance.html)，您的 Athena 查询也具有同样的限制。如果您需要在单一查询中扫描数百万个小对象，则 Amazon S3 可能会对您的查询进行节流。

要避免过度扫描，可使用 AWS Glue ETL 定期压缩文件，或者对表进行分区并添加分区键筛选条件。有关详细信息，请参阅以下资源：
+ [如何配置 AWS Glue ETL 作业以输出较大的文件？](https://aws.amazon.com/premiumsupport/knowledge-center/glue-job-output-large-files/) （*AWS 知识中心*）
+ [以较大的组读取输入文件](https://docs.aws.amazon.com/glue/latest/dg/grouping-input-files.html)（《AWS Glue 开发人员指南**》）

# 优化您的表
<a name="performance-tuning-s3-throttling-optimizing-your-tables"></a>

如果您遇到节流问题，则需要对数据进行结构化。尽管 Amazon S3 可以处理大量数据，但由于数据的结构方式，有时也会发生节流。

以下各节就如何在 Amazon S3 中结构化数据以免出现节流问题提供了一些建议。

## 使用分区
<a name="performance-tuning-s3-throttling-use-partitioning"></a>

您可以使用分区通过限制在任何给定时间必须访问的数据量来减少节流。通过对特定列中的数据进行分区，您可以将请求均匀地分配到多个对象，并减少单一对象的请求数量。可通过减少扫描的数据量提高查询性能并降低成本。

创建表时，您可以定义分区，来充当虚拟列。要在 `CREATE TABLE` 语句中创建包含分区的表，可使用 `PARTITIONED BY (column_name data_type)` 子句定义用于对数据进行分区的键。

要限制查询扫描的分区，可以在查询的 `WHERE` 子句中将其指定为谓词。因此，经常用作筛选条件的列是用于分区的理想候选项。常见做法是根据时间间隔对数据进行分区，这可能会导致多层分区方案。

请注意，分区也有成本。当您增加表中的分区数时，检索和处理分区元数据所需的时间也会增加。因此，过度分区可能会抵消以更明智的方式进行分区带来的好处。如果您的数据严重偏向于一个分区值，并且大多数查询都使用该值，则可能会产生额外开销。

有关在 Athena 中进行分区的更多信息，请参阅 [什么是分区？](ctas-partitioning-and-bucketing-what-is-partitioning.md)。

## 对数据进行分桶
<a name="performance-tuning-s3-throttling-bucket-your-data"></a>

对数据进行分区的另一种方法是对单一分区中的数据进行分桶。进行分桶时，可指定包含要分组到一起的行的一列或多列。然后，将这些行放入多个存储桶中。这样，只需查询必须读取的存储桶即可，从而减少必须扫描的数据行数。

在选择要用于分桶的列时，选择具有高基数（即具有许多不同值）、分布均匀且经常用于筛选数据的列。例如，ID 列等主键就是用于分桶的理想列。

有关在 Athena 中分桶的更多信息，请参阅 [什么是分桶？](ctas-partitioning-and-bucketing-what-is-bucketing.md)。

## 使用 AWS Glue 分区索引
<a name="performance-tuning-s3-throttling-use-aws-glue-partition-indexes"></a>

您可以使用 AWS Glue 分区索引根据一个或多个分区值对表中的数据进行组织。AWS Glue 分区索引可以减少数据传输次数、数据处理量和查询处理时间。

AWS Glue 分区索引是一个元数据文件，其中包含有关表中分区的信息，包括分区键及其值。分区索引存储在 Amazon S3 存储桶中，并在向表中添加新分区时由 AWS Glue 自动更新。

当存在 AWS Glue 分区索引时，查询会尝试获取一部分的分区，而不是加载表中的所有分区。将仅对与查询有关的这部分数据运行查询。

在 AWS Glue 中创建表时，可以基于表中定义的任意分区键组合创建分区索引。在表中创建了一个或多个分区索引后，必须向表中添加用于启用分区筛选的属性。然后，您可以从 Athena 查询表。

有关在 AWS Glue 中创建分区索引的信息，请参阅《AWS Glue 开发人员指南**》中的[在 AWS Glue 中使用分区索引](https://docs.aws.amazon.com/glue/latest/dg/partition-indexes.html)。有关添加表属性以启用分区筛选的信息，请参阅 [使用 AWS Glue 分区索引和筛选来优化查询](glue-best-practices-partition-index.md)。

## 使用数据压缩和文件拆分
<a name="performance-tuning-s3-throttling-use-data-compression-and-file-splitting"></a>

如果文件大小理想或者可以按逻辑组对其进行拆分，则数据压缩可以显著加快查询速度。通常，较高的压缩比需要更多的 CPU 周期才能压缩和解压缩数据。对于 Athena，建议使用 Apache Parquet 或 Apache ORC，以默认压缩数据。有关 Athena 中数据压缩的信息，请参阅 [在 Athena 中使用压缩](compression-formats.md)。

可通过拆分文件允许 Athena 将读取单个文件的任务分配给多个读取器，从而提高并行度。如果单个文件不可拆分，则只有一个读取器可以读取该文件，而其他读取器处于空闲状态。Apache Parquet 和 Apache ORC 也支持可拆分文件。

## 使用经过优化的列式数据存储
<a name="performance-tuning-s3-throttling-use-optimized-columnar-data-stores"></a>

如果将数据转换为列式格式，则会显著提高 Athena 查询性能。生成列式文件时，要考虑的一种优化方法是根据分区键对数据进行排序。

Apache Parquet 和 Apache ORC 是常用的开源列式数据存储。有关将现有 Amazon S3 数据来源转换为其中一种格式的信息，请参阅 [转换为列式格式](columnar-storage.md#convert-to-columnar)。

### 使用较大的 Parquet 块大小或 ORC 条带大小
<a name="performance-tuning-s3-throttling-use-a-larger-parquet-block-size-or-orc-stripe-size"></a>

Parquet 和 ORC 具有诸多数据存储参数，您可以调整这些参数以进行优化。在 Parquet 中，您可以针对块大小进行优化。在 ORC 中，您可以针对条带大小进行优化。数据块或条带越大，可在其中存储的行越多。默认情况下，Parquet 块大小为 128MB，ORC 条带大小为 64MB。

如果 ORC 条带小于 8MB（`hive.orc.max_buffer_size` 的默认值），Athena 会读取整个 ORC 条带。这是 Athena 在列选择性和针对较小条带的每秒输入 / 输出操作之间做出的权衡。

如果表的列数非常多，则较小的数据块或条带大小可能会导致扫描的数据量超出必要范围。在此类情况下，较大的块大小可能更高效。

### 使用 ORC 处理复杂类型
<a name="performance-tuning-s3-throttling-use-orc-for-complex-types"></a>

目前，当您查询在 Parquet 中存储的具有复杂数据类型（例如，`array`、`map` 或 `struct`）的列时，Athena 将读取整行数据，而不是有选择性地仅读取指定列。这是 Athena 中的已知问题。要解决该问题，考虑使用 ORC。

### 选择压缩算法
<a name="performance-tuning-s3-throttling-choose-a-compression-algorithm"></a>

可配置的另一个参数是数据块的压缩算法。有关 Athena 中 Parquet 和 ORC 支持的压缩算法的信息，请参阅 [Athena 压缩支持](https://docs.aws.amazon.com/athena/latest/ug/compression-formats.html)。

有关优化 Athena 列式存储格式的更多信息，请参阅 AWS 大数据博客文章 [Amazon Athena 的十大性能调整技巧](https://aws.amazon.com/blogs/big-data/top-10-performance-tuning-tips-for-amazon-athena/)中的“优化列式数据存储生成”一节。

## 使用 Iceberg 表
<a name="performance-tuning-s3-throttling-use-iceberg-tables"></a>

Apache Iceberg 是一种适用于超大型分析数据集的开放表格式，专为优化 Amazon S3 使用而设计。可使用 Iceberg 表帮助减少 Amazon S3 节流。

Iceberg 表具有以下优势：
+ 可基于一列或多列对 Iceberg 表进行分区。这将优化数据访问并减少查询必须扫描的数据量。
+ 由于 Iceberg 对象存储模式可优化 Iceberg 表以与 Amazon S3 配合使用，因此它可以处理大量数据和繁重的查询工作负载。
+ 对象存储模式下的 Iceberg 表具有可扩展性、容错性和耐用性，这有助于减少节流。
+ ACID 事务支持意味着多个用户可以原子方式添加和删除 Amazon S3 对象。

有关 Apache Iceberg 的更多信息，请参阅 [Apache Iceberg](https://iceberg.apache.org/)。有关在 Athena 中使用 Apache Iceberg 表的更多信息，请参阅[使用 Iceberg 表](https://docs.aws.amazon.com/athena/latest/ug/querying-iceberg.html)。

# 优化您的查询
<a name="performance-tuning-s3-throttling-optimizing-queries"></a>

使用本节中的建议优化 Athena SQL 查询。

## 在 ORDER BY 子句中使用 LIMIT
<a name="performance-tuning-s3-throttling-use-limit-with-the-order-by-clause"></a>

`ORDER BY` 子句按排序顺序返回数据。这要求 Athena 将所有数据行发送到单一 Worker 节点，然后对这些行进行排序。此类查询可能会运行很长时间，甚至会失败。

为了提高查询效率，查看前或后 *N* 个值，然后还要使用 `LIMIT` 子句。这将显著降低排序成本，因为会将排序和限制推送到各个 Worker 节点而不是单一 Worker。

## 优化 JOIN 子句
<a name="performance-tuning-s3-throttling-optimize-join-clauses"></a>

当您联接两个表时，Athena 会将右侧的表分配给 Worker 节点，然后流式处理左侧的表以执行联接。

因此，在联接的左侧指定较大的表，在联接的右侧指定较小的表。这样可减少 Athena 所用的内存并降低查询运行延迟。

同时注意以下几点：
+ 使用多个 `JOIN` 命令时，按从大到小顺序指定表。
+ 除非查询需要，否则避免使用交叉联接。

## 优化 GROUP BY 子句
<a name="performance-tuning-s3-throttling-optimize-group-by-clauses"></a>

`GROUP BY` 运算符根据 `GROUP BY` 列将行分配给 Worker 节点。这些列将在内存中引用，并在载入行时对值进行比较。当 `GROUP BY` 列匹配时，这些值会聚合在一起。考虑到此过程的工作方式，建议按基数从高到低对列进行排序。

## 使用数字代替字符串
<a name="performance-tuning-s3-throttling-use-numbers-instead-of-strings"></a>

由于与字符串相比，数字所需的内存更少且处理速度更快，因此尽可能使用数字代替字符串。

## 限制列数
<a name="performance-tuning-s3-throttling-limit-the-number-of-columns"></a>

要减少存储数据所需的总内存量，限制在 `SELECT` 语句中指定的列数。

## 使用正则表达式代替 LIKE
<a name="performance-tuning-s3-throttling-use-regular-expressions-instead-of-like"></a>

在大型字符串中包含 `LIKE '%string%'` 等子句的查询的计算量可能非常大。在字符串列中筛选多个值时，改用 [regexp\$1like()](https://trino.io/docs/current/functions/regexp.html#regexp_like) 函数和正则表达式。这在您比较一长串值时特别有用。

## 使用 LIMIT 子句
<a name="performance-tuning-s3-throttling-use-the-limit-clause"></a>

在运行查询时，使用 `LIMIT` 子句仅返回所需的列，而不是选择所有列。这减小了通过查询执行管道处理的数据集的大小。当查询包含大量基于字符串的列的表时，建议使用 `LIMIT` 子句。当您对任何查询执行多个联接或聚合时，也建议使用 `LIMIT` 子句。