

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 优化 Apache Iceberg 工作负载的最佳实践
<a name="best-practices"></a>

Iceberg 是一种表格格式，旨在简化数据湖管理并增强工作负载性能。不同的用例可能会优先考虑不同的方面，例如成本、读取性能、写入性能或数据保留，因此 Iceberg 提供了配置选项来管理这些权衡。本节提供了优化和微调 Iceberg 工作负载以满足您的要求的见解。

**Topics**
+ [一般最佳实践](best-practices-general.md)
+ [优化读取性能](best-practices-read.md)
+ [优化写入性能](best-practices-write.md)
+ [优化存储](best-practices-storage.md)
+ [使用压缩来维护表格](best-practices-compaction.md)
+ [在 Amazon S3 中使用冰山工作负载](best-practices-workloads.md)

# 一般最佳实践
<a name="best-practices-general"></a>

无论你的用例如何，当你在上面使用 Apache Iceberg 时 AWS，我们都建议你遵循这些一般的最佳实践。
+ **使用 Iceberg 格式版本 2。**

  默认情况下，Athena 使用 Iceberg 格式版本 2。

  [当你在 Amazon EMR 上使用 Spark 或 AWS Glue 创建 Iceberg 表时，请按照 Iceberg 文档中所述指定格式版本。](https://iceberg.apache.org/docs/nightly/configuration/#reserved-table-properties)
+ **使用 AWS Glue Data Catalog 作为您的数据目录。**

  Athena 默认使用 AWS Glue Data Catalog 。

  当你在 Amazon EMR 上使用 Spark 或 AWS Glue 使用 Iceberg 时，请在你的 Spark 会话中添加以下配置以使用。 AWS Glue Data Catalog有关更多信息，请参阅本指南 AWS Glue前面的 “[Iceberg 的 Spark 配置](iceberg-glue.md#glue-spark-config)” 部分。

  ```
  "spark.sql.catalog.<your_catalog_name>.type": "glue"
  ```
+ **使用 a AWS Glue Data Catalog s 锁定管理器。**

  默认情况下，Athena 对 Ic AWS Glue Data Catalog eberg 表使用作为锁管理器。

  当你在 Amazon EMR 上使用 Spark 或 AWS Glue 使用 Iceberg 时，请务必将你的 Spark 会话配置配置配置配置为使用 AWS Glue Data Catalog 作为锁定管理器。有关更多信息，请参阅 Iceberg 文档中的[乐观锁定](https://iceberg.apache.org/docs/latest/aws/#optimistic-locking)。
+ **使用 Zstandard (ZSTD) 压缩。**

  Iceberg 的默认压缩编解码器是 gzip，可以使用 table 属性对其进行修改。`write.<file_type>.compression-codec`Athena 已经使用 ZSTD 作为 Iceberg 表的默认压缩编解码器。

  一般而言，我们建议使用 ZSTD 压缩编解码器，因为它可以在 GZIP 和 Snappy 之间取得平衡，并且在不影响压缩比的情况下提供良好的 read/write 性能。此外，还可以根据需要调整压缩级别。有关更多信息，请参阅 Athena 文档中的 A [thena 中的 ZSTD 压缩级别](https://docs.aws.amazon.com/athena/latest/ug/compression-support-zstd-levels.html)。

  Snappy 可能提供最佳的整体读取和写入性能，但压缩率低于 GZIP 和 ZSTD。如果您优先考虑性能（即使这意味着要在 Amazon S3 中存储更大的数据量），Snappy 可能是最佳选择。

# 优化读取性能
<a name="best-practices-read"></a>

本节讨论了可以调整的表属性，这些属性可以独立于引擎来优化读取性能。

## 分区
<a name="read-partitioning"></a>

与 Hive 表一样，Iceberg 使用分区作为索引的主要层，以避免读取不必要的元数据文件和数据文件。列统计数据也被视为索引的辅助层，以进一步改进查询计划，从而缩短总体执行时间。

### 对您的数据进行分区
<a name="read-partitioning-data"></a>

要减少查询 Iceberg 表时扫描的数据量，请选择与预期读取模式一致的平衡分区策略：
+ 确定查询中经常使用的列。这些是理想的分区候选对象。例如，如果您通常查询特定日期的数据，则分区列的自然例子就是日期列。
+ 选择低基数分区列以避免创建过多的分区。分区过多会增加表中的文件数量，从而对查询性能产生负面影响。根据经验，“分区过多” 可以定义为大多数分区中的数据大小小于设定值的 2-5 倍的情况。`target-file-size-bytes`

**注意**  
如果您通常通过对高基数列（例如，可以包含数千个值的列）使用过滤器进行查询，请使用带有存储桶转换的 Iceberg 隐藏分区功能，如下一节所述。`id`

### 使用隐藏分区
<a name="read-partitioning-hidden"></a>

如果您的查询通常根据表列的派生项进行筛选，请使用隐藏分区，而不是显式创建新列来用作分区。有关此功能的更多信息，请参阅 [Iceberg 文档](https://iceberg.apache.org/docs/latest/partitioning/#icebergs-hidden-partitioning)。

例如，在具有时间戳列的数据集中（例如，`2023-01-01 09:00:00`），使用分区转换从时间戳中提取日期部分并即时创建这些分区，而不是使用解析后的日期创建新列（例如，`2023-01-01`）。

隐藏分区最常见的用例是：
+ 当数据包含@@ **时间戳列时，按日期或时间进行分区**。Iceberg 提供多种变换来提取时间戳的日期或时间部分。
+ 在@@ **列的哈希函数上进行分**区，此时分区列的基数很高，会导致分区过多。Iceberg 的存储桶转换通过在分区列上使用哈希函数，将多个分区值组合成更少的隐藏（存储桶）分区。

有关所有可用[分区转换](https://iceberg.apache.org/spec/#partition-transforms)的概述，请参阅 Iceberg 文档中的分区转换。

通过使用常规 SQL 函数（如`year()`和），用于隐藏分区的列可以成为查询谓词的一部分。`month()`谓词也可以与诸如`BETWEEN`和`AND`之类的运算符组合使用。

**注意**  
Iceberg 无法对产生不同数据类型的函数执行分区修剪；例如，。`substring(event_time, 1, 10) = '2022-01-01'`

### 使用分区演变
<a name="read-partitioning-evolution"></a>

当现有的[分区策略不是最佳时，请使用 Iceberg 的分区演变](https://iceberg.apache.org/docs/latest/evolution/#partition-evolution)。例如，如果您选择的每小时分区结果太小（每个分区只有几兆字节），请考虑切换到每日或每月分区。

当最初不清楚表的最佳分区策略，并且您想在获得更多见解时完善分区策略时，可以使用这种方法。分区演变的另一个有效用途是，当数据量发生变化并且当前的分区策略随着时间的推移而变得不那么有效时。

有关如何演变分区的说明，请参阅 Iceberg 文档中的 [ALTER TABLE SQL 扩展](https://iceberg.apache.org/docs/latest/spark-ddl/#alter-table-sql-extensions)。 

## 调整文件大小
<a name="read-file-size"></a>

优化查询性能包括最大限度地减少表中的小文件数量。为了获得良好的查询性能，我们通常建议保留大于 100 MB 的 Parquet 和 ORC 文件。

文件大小也会影响 Iceberg 表的查询计划。随着表中文件数量的增加，元数据文件的大小也随之增加。较大的元数据文件可能会导致查询计划变慢。因此，当表大小增加时*，*请增加文件大小****以缓解元数据的指数级扩展。

使用以下最佳实践在 Iceberg 表中创建大小合适的文件。

### 设置目标文件和行组的大小
<a name="read-file-size-target"></a>

Iceberg 提供了以下用于调整数据文件布局的关键配置参数。我们建议您使用这些参数来设置目标文件大小和行组或行标大小。


| **参数** | **默认值** | **评论** | 
| --- |--- |--- |
| `write.target-file-size-bytes` | 512MB | 此参数指定 Iceberg 将创建的最大文件大小。但是，写入某些文件的大小可能小于此限制。 | 
| `write.parquet.row-group-size-bytes` | 128MB | Parquet 和 ORC 都将数据分块存储，因此引擎可以避免在某些操作中读取整个文件。 | 
| `write.orc.stripe-size-bytes` | 64 MB | 
| `write.distribution-mode` | 无，适用于 Iceberg 1.1 及更低版本哈希，从 Iceberg 1.2 版本开始 | Iceberg 要求 Spark 在写入存储之前在其任务之间对数据进行排序。 | 
+ 根据您的预期表格大小，请遵循以下一般准则：
  + **小表**（最多几千兆字节）-将目标文件大小减至 128 MB。还要减小行组或条带大小（例如，减至 8 或 16 MB）。
  + **中型到大型表**（从几千兆字节到几百千兆字节不等）-默认值是这些表的良好起点。如果您的查询选择性很强，请调整行组或条带大小（例如，调整到 16 MB）。
  + **非常大的表**（数百 GB 或 TB）-将目标文件大小增加到 1024 MB 或更多，如果您的查询通常会提取大量数据，请考虑增加行组或条带大小。
+ 为确保写入 Iceberg 表的 Spark 应用程序创建大小合适的文件，请将该`write.distribution-mode`属性设置为`hash`或`range`。有关这些模式之间差异的详细说明，请参阅 Iceberg 文档中的[编写分发模式](https://iceberg.apache.org/docs/latest/spark-writes/#writing-distribution-modes)。

这些是一般指导方针。我们建议您运行测试以确定最适合您的特定表和工作负载的值。

### 定期进行压实
<a name="read-file-size-compaction"></a>

上表中的配置设置了写入任务可以创建的最大文件大小，但不能保证文件会有该大小。为确保文件大小合适，请定期运行压缩，将小文件合并成较大的文件。有关运行压实的详细指导，请参阅本指南后面的[冰山压实](best-practices-compaction.md)。

## 优化列统计信息
<a name="read-column-statistics"></a>

Iceberg 使用列统计信息来执行文件修剪，从而通过减少查询扫描的数据量来提高查询性能。要从列统计数据中受益，请确保 Iceberg 收集查询过滤器中经常使用的所有列的统计信息。

默认情况下，根据表属性的`write.metadata.metrics.max-inferred-column-defaults`定义，Iceberg 仅收集[每个表中前 100 列](https://github.com/apache/iceberg/blob/ae15c7e36973501b40443e75816d3eac39eddc90/core/src/main/java/org/apache/iceberg/TableProperties.java#L276)的统计数据。如果您的表包含超过 100 列，并且您的查询经常引用前 100 列之外的列（例如，您可能有筛选第 132 列的查询），请确保 Iceberg 收集这些列的统计数据。有两种方法可以实现此目的：
+ 创建 Iceberg 表时，对列进行重新排序，使查询所需的列位于设置的列范围内`write.metadata.metrics.max-inferred-column-defaults`（默认为 100）。

  注意：如果您不需要 100 列的统计数据，则可以将`write.metadata.metrics.max-inferred-column-defaults`配置调整为所需的值（例如 20），然后对这些列进行重新排序，使需要读取和写入查询的列位于数据集左侧的前 20 列之内。
+ 如果您在查询筛选器中仅使用几列，则可以禁用指标收集的整体属性，并有选择地选择要为其收集统计数据的各个列，如以下示例所示：

  ```
  .tableProperty("write.metadata.metrics.default", "none")
  .tableProperty("write.metadata.metrics.column.my_col_a", "full")
  .tableProperty("write.metadata.metrics.column.my_col_b", "full")
  ```

注意：当对这些列上的数据进行排序时，列统计信息最为有效。有关更多信息，请参阅本指南后面[的 “设置排序顺序](#read-sort-order)” 部分。

## 选择正确的更新策略
<a name="read-update"></a>

如果您的用例可以接受较慢的写入操作，则使用 copy-on-write策略来优化读取性能。这是 Iceberg 使用的默认策略。

Copy-on-write 从而提高读取性能，因为文件是以读取优化的方式直接写入存储的。但是，与之相比 merge-on-read，每次写入操作花费的时间更长，消耗的计算资源也更多。这在读取和写入延迟之间进行了典型的权衡。通常， copy-on-write非常适合大多数更新并置在同一个表分区中的用例（例如，用于每日批量加载）。

Copy-on-write 配置（`write.update.mode``write.delete.mode`、和`write.merge.mode`）可以在表级别进行设置，也可以在应用程序端独立设置。

## 使用 ZSTD 压缩
<a name="read-compression"></a>

您可以使用 table 属性修改 Iceberg 使用的压缩编解码器。`write.<file_type>.compression-codec`我们建议您使用 ZSTD 压缩编解码器来提高表的整体性能。

默认情况下，Iceberg 1.3 及更早版本使用 GZIP 压缩，与 ZSTD 相比，GZIP 压缩的 read/write 性能较慢。

注意：某些引擎可能使用不同的默认值。使用 At [hena 或 Amazon EMR 7.x 版本创建的 Iceberg 表就是这种](https://docs.aws.amazon.com/athena/latest/ug/compression-support-iceberg.html)情况。

## 设置排序顺序
<a name="read-sort-order"></a>

为了提高 Iceberg 表的读取性能，我们建议您根据查询过滤器中经常使用的一列或多列对表进行排序。排序与 Iceberg 的列统计信息相结合，可以显著提高文件修剪的效率，从而加快读取操作的速度。排序还可以减少使用查询筛选器中排序列的 Amazon S3 查询请求的数量。

通过使用 Spark 运行数据定义语言 (DDL) 语句，可以在表级别设置分层排序顺序。有关可用选项，请参阅 [Iceberg 文档](https://iceberg.apache.org/docs/latest/spark-ddl/#alter-table--write-ordered-by)。设置排序顺序后，写入者会将此排序应用于 Iceberg 表中的后续数据写入操作。

例如，在大多数查询都按日期 (`yyyy-mm-dd`) 分区的表中`uuid`，您可以使用 DDL 选项`Write Distributed By Partition Locally Ordered`来确保 Spark 写入范围不重叠的文件。

下图说明了在对表进行排序时如何提高列统计数据的效率。在示例中，排序后的表只需要打开一个文件，并且可以最大限度地受益于 Iceberg 的分区和文件。在未排序的表中，任何数据文件中都`uuid`可能存在任何数据，因此查询必须打开所有数据文件。

![\[在 Iceberg 表中设置排序顺序\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/apache-iceberg-on-aws/images/setting-sort-order.png)


更改排序顺序不会影响现有的数据文件。你可以使用 Iceberg compaction 对它们应用排序顺序。

如下图所示，使用 Iceberg 排序的表格可能会降低工作负载成本。

![\[Iceberg 和 Parquet 桌子的比较成本\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/apache-iceberg-on-aws/images/cost-graph.png)


这些图表汇总了运行Hive（Parquet）表的TPC-H基准测试与Iceberg排序表的结果。但是，对于其他数据集或工作负载，结果可能会有所不同。

![\[Parquet vs Iceberg 表的 TPC-H 基准测试结果\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/apache-iceberg-on-aws/images/s3-api-calls.png)


# 优化写入性能
<a name="best-practices-write"></a>

本节讨论表属性，您可以调整这些属性以优化 Iceberg 表的写入性能，不受引擎影响。

## 设置表格分发模式
<a name="write-distribution-mode"></a>

Iceberg 提供多种写入分配模式，用于定义写入数据在 Spark 任务中的分布方式。有关可用模式的概述，请参阅 Iceberg 文档中的[编写分发模式](https://iceberg.apache.org/docs/latest/spark-writes/#writing-distribution-modes)。

对于优先考虑写入速度的用例，尤其是在流式工作负载中，`write.distribution-mode`请设置为`none`。这样可以确保 Iceberg 不会请求额外的 Spark 洗牌，并且可以在 Spark 任务中可用时写入数据。此模式特别适用于 Spark 结构化流媒体应用程序。

**注意**  
将写入分配模式设置为`none`往往会生成大量小文件，从而降低读取性能。我们建议定期进行压缩，将这些小文件整合到大小合适的文件中，以提高查询性能。

## 选择正确的更新策略
<a name="write-update-strategy"></a>

当您的用例可以接受对最新数据执行较慢的读取操作时，请使用 merge-on-read****策略来优化写入性能。

使用时 merge-on-read，Iceberg 会将更新和删除内容作为单独的小文件写入存储器。读取表格时，读者必须将这些更改与基础文件合并，以返回最新的数据视图。这会导致读取操作的性能下降，但会加快更新和删除的写入速度。通常， merge-on-read对于具有更新的流式处理工作负载或更新较少且分布在多个表分区中的任务来说，它是理想的选择。

可以在表级别设置 merge-on-read配置（`write.update.mode``write.delete.mode`、和`write.merge.mode`），也可以在应用程序端单独设置。

使用 merge-on-read需要定期进行压缩，以防止读取性能随着时间的推移而下降。Compaction 可将更新和删除与现有数据文件进行协调，以创建一组新的数据文件，从而消除读取方面产生的性能损失。默认情况下，除非您将`delete-file-threshold`属性的默认值更改为较小的值，否则Iceberg的压缩不会合并删除文件（请参阅 [Iceberg](https://iceberg.apache.org/docs/latest/spark-procedures/#rewrite_data_files) 文档）。要了解有关压实的更多信息，请参阅本指南后面的[冰山压实](best-practices-compaction.md)部分。

## 选择正确的文件格式
<a name="write-file-format"></a>

Iceberg 支持以 Parquet、ORC 和 Avro 格式写入数据。镶木地板是默认格式。Parquet 和 ORC 是列式格式，可提供卓越的读取性能，但通常写入速度较慢。这代表了读取和写入性能之间的典型权衡。

如果写入速度对您的用例很重要，例如在流媒体工作负载中，请考虑在编写器的选项`Avro`中设置为`write-format`，以 Avro 格式写入。由于 Avro 是一种基于行的格式，因此它以较慢的读取性能为代价提供更快的写入时间。

要提高读取性能，请定期进行压缩，将小 Avro 文件合并并转换为较大的 Parquet 文件。压实过程的结果受制于`write.format.default`表格设置。Iceberg 的默认格式是 Parquet，因此，如果你用 Avro 编写然后运行压缩，Iceberg 会将 Avro 文件转换为 Parquet 文件。示例如下：

```
spark.sql(f"""
    CREATE TABLE IF NOT EXISTS glue_catalog.{DB_NAME}.{TABLE_NAME} (
        Col_1 float, 
        <<<…other columns…>>
        ts timestamp)
    USING iceberg
    PARTITIONED BY (days(ts))
    OPTIONS (
      'format-version'='2',
      write.format.default'=parquet)
""")

query = df \
    .writeStream \
    .format("iceberg") \
    .option("write-format", "avro") \
    .outputMode("append") \
    .trigger(processingTime='60 seconds') \
    .option("path", f"glue_catalog.{DB_NAME}.{TABLE_NAME}") \
    .option("checkpointLocation", f"s3://{BUCKET_NAME}/checkpoints/iceberg/")

    .start()
```

# 优化存储
<a name="best-practices-storage"></a>

更新或删除 Iceberg 表中的数据会增加数据的副本数量，如下图所示。运行压缩也是如此：它增加了 Amazon S3 中的数据副本数量。这是因为 Iceberg 将所有表的底层文件视为不可变的。

![\[更新或删除 Iceberg 表中数据的结果\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/apache-iceberg-on-aws/images/optimizing-storage.png)


按照本节中的最佳实践来管理存储成本。

## 启用 S3 智能分层
<a name="storage-s3-intelligent-tiering"></a>

当访问模式发生变化时，使用 [Amazon S3 智能分层](https://docs.aws.amazon.com/AmazonS3/latest/userguide/intelligent-tiering-overview.html)存储类自动将数据移至最具成本效益的访问层。此选项没有操作开销或对性能的影响。 

注意：请勿在带有 Iceberg 表的 S3 智能分层中使用可选层（例如存档访问和深度存档访问权限）。要存档数据，请参阅下一节中的指南。

您也可以使用 [Amazon S3 生命周期规则来设置自己的规则](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt.html)，以便将对象移动到其他 Amazon S3 存储类别，例如 S3 标准-IA 或 S3 One Zone-IA（请参阅 Amazon S3 文档中[支持的过渡和相关限制](https://docs.aws.amazon.com/AmazonS3/latest/userguide/lifecycle-transition-general-considerations.html#lifecycle-general-considerations-transition-sc)）。

## 存档或删除历史快照
<a name="storage-snapshots"></a>

对于向 Iceberg 表提交的每笔事务（插入、更新、合并、压缩），都会创建该表的新版本或快照。随着时间的推移，Amazon S3 中的版本数量和元数据文件数量会不断增加。

快照隔离、表回滚和时空旅行查询等功能需要保留表的快照。但是，存储成本会随着您保留的版本数量的增加而增加。

下表描述了您可以实施的设计模式，以根据您的数据保留要求管理成本。


| **设计模式** | **解决方案** | **使用案例** | 
| --- |--- |--- |
| **删除旧快照** |   使用 Athena 中的 [VACUUM 语句](https://docs.aws.amazon.com/athena/latest/ug/vacuum-statement.html)删除旧的快照。此操作不会产生任何计算成本。    [或者，你可以在亚马逊 EMR 上使用 Spark 或 AWS Glue 删除快照。有关更多信息，请参阅 Iceberg 文档中的 expire\$1snapshots。](https://iceberg.apache.org/docs/latest/spark-procedures/#expire_snapshots)   | 这种方法会删除不再需要的快照以降低存储成本。您可以根据数据保留要求配置应保留多少快照或保留多长时间。此选项对快照执行硬删除。您无法回滚或穿越到过期的快照。 | 
| **为特定快照设置保留策略** |   在 Iceberg 中使用标签标记特定的快照并定义保留策略。有关更多信息，请参阅 Iceberg 文档中的[历史标签](https://iceberg.apache.org/docs/latest/branching/#historical-tags)。 例如，您可以在 Amazon EMR 上的 Spark 中使用以下 SQL 语句，每月保留一个快照，为期一年： <pre>ALTER TABLE glue_catalog.db.table <br />CREATE TAG 'EOM-01' AS OF VERSION 30 RETAIN 365 DAYS</pre>   在 Amazon EMR 上使用 Spark 或 AWS Glue 删除剩余的未加标签的中间快照。   | 这种模式有助于遵守业务或法律要求，这些要求您显示过去给定时刻的表格状态。通过对特定的带标签的快照设置保留政策，您可以删除已创建的其他（未标记的）快照。这样，您无需保留创建的每个快照即可满足数据保留要求。 | 
| **存档旧快照** |   使用 Amazon S3 标签用 Spark 标记对象。（Amazon S3 标签与 Iceberg 标签不同；有关更多信息，请参阅 [Iceberg 文档](https://iceberg.apache.org/docs/latest/aws/#s3-tags)。） 例如： <pre>spark.sql.catalog.my_catalog.s3.delete-enabled=false and \<br />spark.sql.catalog.my_catalog.s3.delete.tags.my_key=to_archive</pre>   在 Amazon EMR 上使用 Spark 或[删除 AWS Glue](https://iceberg.apache.org/docs/latest/spark-procedures/#expire_snapshots)快照。当您使用示例中的设置时，此过程会标记对象并将其与 Iceberg 表元数据分离，而不是将其从 Amazon S3 中删除。   使用 S3 生命周期规则将标记为 `to_archive` S [3 Glacier 存储类](https://docs.aws.amazon.com/amazonglacier/latest/dev/introduction.html)之一的对象过渡。   要查询存档数据，请执行以下操作：   [恢复存档对象](https://docs.aws.amazon.com/AmazonS3/latest/userguide/restoring-objects.html)（如果对象已转换为 Amazon Glacier 即时检索存储类别，则无需执行此步骤）。   使用 Iceberg 中的 [register\$1table 过程](https://iceberg.apache.org/docs/latest/spark-procedures/#register_table)将快照注册为目录中的表。    有关详细说明，请参阅 AWS 博客文章[提高在 Amazon S3 数据湖上构建的 Apache Iceberg 表的操作效率](https://aws.amazon.com/blogs/big-data/improve-operational-efficiencies-of-apache-iceberg-tables-built-on-amazon-s3-data-lakes/)。  | 这种模式允许您以较低的成本保留所有表版本和快照。如果不先将这些版本恢复为新表，就无法穿越时空或回滚到存档的快照。出于审计目的，这通常是可以接受的。您可以将此方法与以前的设计模式相结合，为特定的快照设置保留策略。 | 

## 删除孤立文件
<a name="storage-orphan-files"></a>

在某些情况下，Iceberg 应用程序可能会在您提交事务之前失败。这会将数据文件留在 Amazon S3 中。由于没有提交，因此这些文件不会与任何表相关联，因此您可能需要异步清理它们。

要处理这些删除，您可以使用亚马逊 Athena 中的 V [ACUUM 语句](https://docs.aws.amazon.com/athena/latest/ug/vacuum-statement.html)。此语句删除快照并删除孤立文件。这非常具有成本效益，因为 Athena 不收取此操作的计算成本。此外，在使用该`VACUUM`语句时，您不必安排任何其他操作。

或者，您可以在 Amazon EMR 上使用 Spark 或 AWS Glue 运行该程序。`remove_orphan_files`此操作会产生计算成本，并且必须单独进行计划。有关更多信息，请参阅 [Iceberg 文档](https://iceberg.apache.org/docs/latest/spark-procedures/#remove_orphan_files)。

# 使用压缩来维护表格
<a name="best-practices-compaction"></a>

Iceberg 包含的功能使您能够在向[表写入数据后执行表维护操作](https://iceberg.apache.org/docs/latest/maintenance/)。一些维护操作侧重于简化元数据文件，而另一些维护操作则增强了数据在文件中的聚集方式，以便查询引擎可以有效地找到响应用户请求所需的信息。本节重点介绍与压缩相关的优化。

## 冰山压实
<a name="iceberg-compaction"></a>

在 Iceberg 中，您可以使用压实来执行四项任务：
+ 将小文件合并成通常超过 100 MB 的大文件。这种技术被称为*垃圾箱包装*。
+ 将删除文件与数据文件合并。删除文件是由使用该 merge-on-read方法的更新或删除生成的。
+ （重新）根据查询模式对数据进行排序。可以在没有任何排序顺序的情况下写入数据，也可以使用适合写入和更新的排序顺序写入数据。
+ 使用空间填充曲线对数据进行聚类，以优化不同的查询模式，尤其是 z 顺序排序。

开启后 AWS，你可以通过 Amazon Athena 或者在亚马逊 EMR 中使用 Spark 或。 AWS Glue

使用 [rewrite\$1data\$1files](https://iceberg.apache.org/docs/latest/spark-procedures/#rewrite_data_files) 过程运行压缩时，可以调整多个旋钮来控制压缩行为。下图显示了装箱的默认行为。了解垃圾箱压缩是理解分层排序和 Z 顺序排序实现的关键，因为它们是垃圾箱打包界面的扩展，操作方式类似。主要区别在于对数据进行排序或聚类所需的额外步骤。

![\[Iceberg 表中的默认垃圾箱打包行为\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/apache-iceberg-on-aws/images/compaction.png)


在此示例中，Iceberg 表由四个分区组成。每个分区的大小不同，文件数量也不同。如果您启动 Spark 应用程序来运行压缩，则该应用程序总共会创建四个要处理的文件组。文件组是 Iceberg 抽象，它表示将由单个 Spark 作业处理的一组文件。也就是说，运行压缩的 Spark 应用程序将创建四个 Spark 作业来处理数据。

## 调整压实行为
<a name="compaction-behavior"></a>

以下关键属性控制如何选择数据文件进行压缩：
+ 默认情况下，[MAX\$1FILE\$1GROUP\$1SIZE\$1BYTES 将单个文件组（Spark 作业）的数据限制设置为 100 GB](https://iceberg.apache.org/javadoc/1.2.0/org/apache/iceberg/actions/RewriteDataFiles.html#MAX_FILE_GROUP_SIZE_BYTES)。对于没有分区的表或分区跨越数百 GB 的表，此属性尤其重要。通过设置此限制，您可以分解操作以计划工作并取得进展，同时防止群集上的资源耗尽。

  注意：每个文件组都是单独排序的。因此，如果要执行分区级排序，则必须调整此限制以匹配分区大小。
+ [MIN\$1FILE\$1SIZE\$1BYTES 或 MIN\$1](https://iceberg.apache.org/javadoc/1.2.0/org/apache/iceberg/actions/BinPackStrategy.html#MIN_FILE_SIZE_BYTES) [FILE\$1SIZE\$1DEFAULT\$1RATIO 默认为在表级别设置的目标文件大小的 7](https://iceberg.apache.org/javadoc/1.2.0/org/apache/iceberg/actions/BinPackStrategy.html#MIN_FILE_SIZE_DEFAULT_RATIO) 5%。例如，如果表的目标大小为 512 MB，则任何小于 384 MB 的文件都将包含在要压缩的文件集中。
+ [MAX\$1FILE\$1SIZE\$1BYTES 或 MAX\$1FILE\$1](https://iceberg.apache.org/javadoc/1.2.0/org/apache/iceberg/actions/BinPackStrategy.html#MAX_FILE_SIZE_BYTES) [SIZE\$1DEFAULT\$1RATIO 默认为目标文件大小的180％](https://iceberg.apache.org/javadoc/1.2.0/org/apache/iceberg/actions/BinPackStrategy.html#MAX_FILE_SIZE_DEFAULT_RATIO)。与设置最小文件大小的两个属性一样，这些属性用于识别压缩作业的候选文件。
+ [MIN\$1INP](https://iceberg.apache.org/javadoc/1.2.0/org/apache/iceberg/actions/BinPackStrategy.html#MIN_INPUT_FILES) UT\$1FILES 指定表分区大小小于目标文件大小时要压缩的最小文件数。此属性的值用于确定是否值得根据文件数（默认为 5）压缩文件。
+ D@@ [ELETE\$1FILE\$1TH](https://iceberg.apache.org/javadoc/1.2.0/org/apache/iceberg/actions/BinPackStrategy.html#DELETE_FILE_THRESHOLD) RESHOLD 指定文件在压缩中包含之前对其执行的最小删除操作次数。除非您另行指定，否则压缩不会将删除文件与数据文件合并。要启用此功能，必须使用此属性设置阈值。此阈值特定于单个数据文件，因此，如果将其设置为 3，则仅当有三个或更多引用数据文件的删除文件时，才会重写该数据文件。

这些属性可以深入了解上图中文件组的形成情况。

例如，标记为的分区`month=01`包括两个文件组，因为它超过了 100 GB 的最大大小限制。相比之下，该`month=02`分区包含单个文件组，因为它小于 100 GB。该`month=03`分区不满足默认的最低输入文件要求，即五个文件。因此，它不会被压实。最后，尽管该`month=04`分区包含的数据不足以形成所需大小的单个文件，但由于该分区包含五个以上的小文件，因此文件将被压缩。

您可以为在亚马逊 EMR 上运行的 Spark 设置这些参数，或者。 AWS Glue对于 Amazon Athena，您可以使用以前缀开头[的表](https://docs.aws.amazon.com/athena/latest/ug/querying-iceberg-creating-tables.html#querying-iceberg-table-properties)属性来管理类似的属性）。`optimize_`

## 在亚马逊 EMR 上使用 Spark 运行压缩或 AWS Glue
<a name="compaction-emr-glue"></a>

本节介绍如何正确调整 Spark 集群的大小以运行 Iceberg 的压缩实用程序。以下示例使用 Amazon EMR Serverless，但您可以在 EC2 或 EKS 上的 Amazon EMR 中或中使用相同的方法。 AWS Glue

您可以利用文件组和 Spark 作业之间的关联来规划群集资源。要按顺序处理文件组，考虑到每个文件组的最大大小为 100 GB，可以设置以下 [Spark](https://docs.aws.amazon.com/emr/latest/EMR-Serverless-UserGuide/jobs-spark.html#spark-defaults) 属性：
+ `spark.dynamicAllocation.enabled` = `FALSE`
+ `spark.executor.memory` = `20 GB`
+ `spark.executor.instances` = `5`

如果要加快压缩速度，则可以通过增加并行压缩的文件组的数量来横向扩展。您还可以使用手动或动态扩展来扩展 Amazon EMR。
+ **手动缩放**（例如，按系数 4）
  + `MAX_CONCURRENT_FILE_GROUP_REWRITES`=`4`（我们的因素）
  + `spark.executor.instances`=`5`（示例中使用的值）x`4`（我们的因子）= `20`
  + `spark.dynamicAllocation.enabled` = `FALSE`
+ **动态缩放**
  + `spark.dynamicAllocation.enabled`=`TRUE `（默认，无需执行任何操作）
  + [MAX\$1CONCURRENT\$1FILE\$1GROUP\$1RE](https://iceberg.apache.org/javadoc/1.2.0/org/apache/iceberg/actions/RewriteDataFiles.html#MAX_CONCURRENT_FILE_GROUP_REWRITES) WRITES =`N `（将此值与`spark.dynamicAllocation.maxExecutors`，默认值为 100；根据示例中的执行器配置，您可以将其设置为 20）`N`

  这些是帮助调整集群规模的指导方针。但是，您还应该监控 Spark 作业的性能，以找到最适合您的工作负载的设置。

## 使用亚马逊 Athena 进行压缩
<a name="compaction-athena"></a>

[Athena 通过 OPTIMIZE 语句提供了 Iceberg 压缩实用程序作为托管功能的实现。](https://docs.aws.amazon.com/athena/latest/ug/optimize-statement.html)您可以使用此语句来运行压缩，而不必评估基础架构。

此语句使用垃圾箱打包算法将小文件分组为较大的文件，并将删除文件与现有数据文件合并。要使用分层排序或 z 顺序排序对数据进行聚类，请在 Amazon EM AWS Glue R 上使用 Spark 或。

可以在创建表时通过在`OPTIMIZE`语句中传递表属性来更改该语句的默认行为，也可以在创建表之后使用该`CREATE TABLE`语句来更改该`ALTER TABLE`语句的默认行为。有关默认值，请参阅 [Athena 文档。](https://docs.aws.amazon.com/athena/latest/ug/querying-iceberg-creating-tables.html#querying-iceberg-table-properties)

## 跑步压实的建议
<a name="compaction-recommendations"></a>


| **使用案例** | **建议** | 
| --- |--- |
| **按计划运行垃圾箱压实** |   如果您不知道`OPTIMIZE`表中包含多少小文件，请使用 Athena 中的语句。Athena 的定价模型基于扫描的数据，因此，如果没有要压缩的文件，则不会产生与这些操作相关的成本。为避免在 Athena 表上遇到超时，`OPTIMIZE`请按基础运行。 per-table-partition   如果您希望压缩大量小文件 AWS Glue ，请使用 Amazon EMR 或动态扩展。   | 
| **根据事件对垃圾箱进行装箱压实** |   如果您希望压缩大量小文件 AWS Glue ，请使用 Amazon EMR 或动态扩展。   | 
| **运行压缩来对数据进行排序** |   使用 Amazon EMR 或 AWS Glue，因为排序是一项昂贵的操作，可能需要将数据泄漏到磁盘。   | 
| **运行压缩以使用 z 顺序排序对数据进行聚类** |   使用 Amazon EMR 或 AWS Glue，因为 z 顺序排序是一项非常昂贵的操作，可能需要将数据泄漏到磁盘。   | 
| **在由于数据迟到而可能被其他应用程序更新的分区上运行压缩** |   使用 Amazon EMR 或。 AWS Glue启用 Iceberg [部分\$1PROGRESS\$1ENABLED 属性](https://iceberg.apache.org/javadoc/1.2.0/org/apache/iceberg/actions/RewriteDataFiles.html#PARTIAL_PROGRESS_ENABLED)。使用此选项时，Iceberg 会将压缩输出拆分为多个提交。如果发生冲突（也就是说，如果在压缩运行时更新了数据文件），则此设置将重试限制为包含受影响文件的提交，从而降低了重试成本。否则，您可能需要重新压缩所有文件。   | 
| **在冷分区（不再接收活动写入的数据分区）上运行压缩** |   使用 Amazon EMR 或。 AWS Glue在此`rewrite_data_files`过程中，指定排除主动写入分区的`where`谓词。这种策略可以防止写入器和压缩作业之间的数据冲突，只留下 Iceberg 可以自动解决的元数据冲突。   | 

# 在 Amazon S3 中使用冰山工作负载
<a name="best-practices-workloads"></a>

本节讨论了可用于优化 Iceberg 与 Amazon S3 交互的 Iceberg 属性。

## 防止热分区（HTTP 503 错误）
<a name="workloads-503"></a>

在 Amazon S3 上运行的某些数据湖应用程序可以处理数百万或数十亿个对象并处理数 PB 的数据。这可能导致前缀接收大量流量，通常通过 HTTP 503（服务不可用）错误检测到这些流量。为防止出现此问题，请使用以下 Iceberg 属性：
+ 设置`write.distribution-mode`为`range`左`hash`右，Iceberg 会写入大文件，从而减少 Amazon S3 请求。这是首选配置，应能解决大多数情况。
+ 如果由于工作负载中有大量数据而继续出现 503 错误，则可以在 Iceberg `true` 中`write.object-storage.enabled`将其设置为。这指示 Iceberg 对对象名称进行哈希处理，并将负载分配到多个随机的 Amazon S3 前缀中。

有关这些属性的更多信息，请参阅 Iceberg 文档中的[写入属性](https://iceberg.apache.org/docs/latest/configuration/#write-properties)。

## 使用 Iceberg 维护操作来释放未使用的数据
<a name="workloads-unused-data"></a>

要管理 Iceberg 表，你可以使用 Iceberg 核心 API、Iceberg 客户端（例如 Spark）或托管服务，例如亚马逊 Athena。要从 Amazon S3 中删除旧文件或未使用的文件，我们建议您仅使用 Iceberg 原生文件 APIs 来[删除快照](https://iceberg.apache.org/docs/latest/maintenance/#expire-snapshots)、[移除旧的元数据文件](https://iceberg.apache.org/docs/latest/maintenance/#remove-old-metadata-files)和[删除孤立](https://iceberg.apache.org/docs/latest/maintenance/#delete-orphan-files)文件。

 APIs 通过 Boto3、Amazon S3 软件开发工具包或 AWS Command Line Interface (AWS CLI) 使用 Amazon S3，或者使用任何其他非 Iceberg 方法覆盖或删除 Iceberg 表的 Amazon S3 文件会导致表损坏和查询失败。

## 跨复制数据 AWS 区域
<a name="workloads-replication"></a>

在 Amazon S3 中存储 Iceberg 表时，您可以使用 Amazon S3 中的内置功能，例如[跨区域复制 (CRR)](https://docs.aws.amazon.com/AmazonS3/latest/userguide/replication.html) 和[多区域接入点 (MRAP)](https://docs.aws.amazon.com/AmazonS3/latest/userguide/MultiRegionAccessPoints.html)，跨多个区域复制数据。 AWS 区域 MRAP 为应用程序提供了一个全局端点，用于访问位于多个存储桶中的 S3 存储桶。 AWS 区域 Iceberg 不支持相对路径，但您可以使用 MRAP 通过将存储桶映射到接入点来执行 Amazon S3 操作。MRAP 还与 Amazon S3 跨区域复制流程无缝集成，后者会导致长达 15 分钟的延迟。您必须同时复制数据和元数据文件。

**重要**  
目前，Iceberg 与 MRAP 的集成仅适用于 Apache Spark。如果您需要故障转移到辅助服务器 AWS 区域，则必须计划将用户查询重定向到故障转移区域中的 Spark SQL 环境（例如 Amazon EMR）。

CRR 和 MRAP 功能可帮助您为 Iceberg 表构建跨区域复制解决方案，如下图所示。

![\[Iceberg 表的跨区域复制\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/apache-iceberg-on-aws/images/cross-region-replication.png)


要设置此跨区域复制架构，请执行以下操作：

1. 使用 MRAP 位置创建表。这样可以确保 Iceberg 元数据文件指向 MRAP 位置而不是物理存储桶位置。

1. 使用 Amazon S3 MRAP 复制 Iceberg 文件。 ****MRAP 支持数据复制，服务级别协议 (SLA) 为 15 分钟。Iceberg 可防止读取操作在复制过程中引入不一致。

1. 使表在辅助区域 AWS Glue Data Catalog 中可用。可从两个选项中选择：
   + 使用 AWS Glue Data Catalog 复制设置用于复制 Iceberg 表元数据的管道。此实用程序在 GitHub [Glue Catalog 和 Lake Formation 权限复制](https://github.com/aws-samples/lake-formation-pemissions-sync)存储库中可用。这种事件驱动的机制根据事件日志复制目标区域中的表。
   + 当您需要进行故障转移时，请在辅助区域中注册表。对于此选项，您可以使用之前的实用程序或 Iceberg regi [ster\$1table 过程](https://iceberg.apache.org/docs/latest/spark-procedures/#register_table)并将其指向最新的文件。`metadata.json`