Athena 中的性能优化 - Amazon Athena
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

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

Athena 中的性能优化

本主题提供了有关提高 Athena 查询性能的一般信息和具体建议,以及如何解决与限制和资源使用情况相关的错误。

服务限额

Athena 对查询运行时间、账户中的并发查询数量和 API 请求速率等指标强制实施限额。有关限额的更多信息,请参阅 服务限额。超过这些限额会导致查询失败,无论是在提交时还是在查询执行过程中。

本页上的许多性能优化提示可以帮助缩短查询的运行时间。优化可以释放容量,这样您就可以在并发限额内运行更多查询,并防止查询因运行时间过长而被取消。

并发查询和 API 请求数量的限额按 Amazon Web Services 账户 和 Amazon Web Services 区域 计算。我们建议每个 Amazon Web Services 账户 运行一个工作负载(或使用单独的预调容量预留),以防止工作负载争夺相同的限额。

如果您在同一个账户中运行两个工作负载,则其中一个工作负载可能会运行大量查询。这可能会导致剩余的工作负载受到限制或阻止,无法运行查询。为避免这种情况,您可以将工作负载转移到不同的账户,为每个工作负载提供自己的并发限额。为其中一个或两个工作负载创建预调配容量预留可以实现相同的目标。

其他服务的限额

当 Athena 运行查询时,它可以调用其他强制实施限额的服务。在查询执行期间,Athena 可以对 Amazon Glue Data Catalog、Amazon S3 以及其他 Amazon 服务(如 IAM 和 Amazon KMS)进行 API 调用。如果您使用联合查询,Athena 也会调用 Amazon Lambda。所有这些服务都有自己的限制和限额,可以超出。当查询执行遇到来自这些服务的错误时,查询会失败并包括来自源服务的错误。系统会重试可恢复的错误;但如果问题不能及时解决,查询仍可能失败。请务必仔细阅读错误消息,以确定它们是来自 Athena 还是来自其他服务。本文档介绍了一些相关错误。

有关如何解决由 Amazon S3 服务限额导致的错误的更多信息,请参阅本文档后面的 避免文件过多。有关 Amazon S3 性能优化的更多信息,请参阅《Amazon S3 用户指南》中的最佳实践设计模式:优化 Amazon S3 性能

资源限制

Athena 在分布式查询引擎中运行查询。当您提交查询时,Athena 引擎查询计划程序会估计运行查询所需的计算容量,并相应地准备计算节点集群。有些查询(例如 DDL 查询)只能在一个节点上运行。对大型数据集的复杂查询在更大的集群上运行。节点是统一的,具有相同的内存、CPU 和磁盘配置。Athena 横向扩展,而非纵向扩展,以处理要求更高的查询。

有时,查询的需求会超过运行查询的集群可用的资源。发生这种情况时,查询会失败,并显示错误查询在此缩放系数耗尽了资源

最常耗尽的资源是内存,但在极少数情况下,也可能是磁盘空间。内存错误通常发生在引擎执行联接或窗口函数时,但也可能发生在不同的计数和聚合中。

即使查询因出现“资源不足”错误而失败一次,当您再次运行它时,它也可能会成功。查询执行的确定性不定。加载数据需要多长时间以及中间数据集在节点上的分布方式等因素可能会导致不同的资源使用情况。例如,假设一个查询连接两个表,并且在连接条件的值分布中存在严重偏差。这样的查询在大多数情况下都可以成功,但是当最常见的值最终由同一个节点处理时,偶尔会失败。

为防止您的查询超出可用资源,请使用本文档中提到的性能调整提示。特别是,有关如何优化耗尽可用资源的查询的提示,请参阅 优化连接优化窗口函数使用近似值优化查询

查询优化技术

使用本节中介绍的查询优化技巧来加快查询的运行速度,或者将其作为超出 Athena 资源限制的查询的变通方法。

优化连接

在分布式查询引擎中执行联接有许多不同的策略。其中最常见的两种是分布式哈希联接和具有复杂联接条件的查询。

分布式哈希联接

最常见的连接类型使用等式比较作为连接条件。Athena 以分布式哈希联接的形式运行这种联接。

在分布式哈希联接中,引擎从联接的一端构建查找表(哈希表)。该端称为构建端。构建端的记录分布在各个节点上。每个节点都为其子集生成一个查找表。然后,联接的另一端(称为探测端)通过节点进行流式传输。探测端的记录以与构建端相同的方式分布在节点上。这使每个节点都能够通过在自己的查找表中查找匹配的记录来执行联接。

当从联接的构建端创建的查找表无法存入内存时,查询会失败。即使构建端的总大小小于可用内存,如果记录的分布存在明显偏差,查询也会失败。在极端情况下,所有记录的联接条件值可能相同,并且必须放入单个节点上的内存中。如果将一组值发送到同一个节点,并且这些值加起来超过可用内存,则即使是偏差较小的查询也会失败。节点确实能够将记录溢出到磁盘,但是溢出会减慢查询的执行速度,可能不足以防止查询失败。

Athena 尝试重新排序联接,使用较大的关系作为探测端,将较小的关系用作构建端。但是,由于 Athena 不管理表中的数据,因此它的信息有限,通常必须假设第一个表更大,第二个表更小。

使用基于等式的联接条件编写联接时,假设 JOIN 关键字左侧的表是探测端,右侧的表是构建端。确保右侧的表(即构建端)是表格中较小的一个。如果无法将联接的构建端缩小到足以放入内存,请考虑运行多个查询来联接构建表的子集。

其他联接类型

具有复杂联接条件的查询(例如,使用 LIKE> 或其他运算符的查询)通常对计算要求很高。在最糟糕的情况下,必须将联接一侧的每条记录与联接另一侧的每条记录进行比较。由于执行时间随记录数的平方而增长,因此此类查询有可能超过最大执行时间。

要提前了解 Athena 将如何执行您的查询,您可以使用 EXPLAIN 语句。有关更多信息,请参阅 在 Athena 中使用 EXPLAIN 和 EXPLAIN ANALYZE了解 Athena EXPLAIN 语句结果

优化窗口函数

由于窗口函数是资源密集型操作,因此它们可能会使查询运行缓慢甚至失败,并显示消息查询在此缩放系数耗尽了资源。窗口函数将它们操作的所有记录保存在内存中,以便计算其结果。当窗口非常大时,窗口函数可能会耗尽内存。

要确保查询在可用内存限制内运行,请减小窗口函数操作的窗口的大小。为此,您可以添加 PARTITIONED BY 子句或缩小现有分区子句的范围。

改用非窗口函数

有时,使用窗口函数的查询可以在没有窗口函数的情况下重写。例如,您可以使用 ORDER BYLIMIT,而不是使用 row_number 来查找前 N 条记录。您可以使用 max_bymin_by任意聚合函数,而不是使用 row_numberrank 删除重复记录。

例如,假设您有一个包含来自传感器的更新的数据集。传感器会定期报告其电池状态,并包含一些元数据(例如位置)。如果您想知道每个传感器的上次电池状态及其位置,可以使用以下查询:

SELECT sensor_id, arbitrary(location) AS location, max_by(battery_status, updated_at) AS battery_status FROM sensor_readings GROUP BY sensor_id

由于每条记录的位置等元数据都相同,因此您可以使用 arbitrary 函数从组中选取任何值。

要获取最后电池状态,您可以使用 max_by 函数。max_by 函数从发现另一列的最大值的记录中选取一列的值。在这种情况下,它将返回记录的电池状态以及组内最后一次更新时间。与使用窗口函数的等效查询相比,此查询运行速度更快,占用的内存也更少。

优化聚合

当 Athena 执行聚合时,它会使用 GROUP BY 子句中的列在工作节点之间分配记录。为了尽可能高效地将记录与组进行匹配,节点会尝试将记录保存在内存中,但必要时会将其溢出到磁盘。

避免在 GROUP BY 子句中包含多余的列也是个不错的做法。由于较少的列需要更少的内存,因此使用较少的列描述组的查询效率更高。数字列使用的内存也比字符串少。例如,当您聚合同时具有数字类别 ID 和类别名称的数据集时,在 GROUP BY 子句中仅使用类别 ID 列。

有时,查询会在 GROUP BY 子句中包含列,以避免列必须是 GROUP BY 子句的一部分或是聚合表达式。如果不遵守此规则,您可能会收到如下错误消息:

EXPRESSION_NOT_AGGREGATE:第 1:8 行:'category' 必须是聚合表达式或出现在 GROUP BY 子句中

为避免在 GROUP BY 子句中添加冗余列,可以使用任意函数,如下例所示。

SELECT country_id, arbitrary(country_name) AS country_name, COUNT(*) AS city_count FROM world_cities GROUP BY country_id

ARBITRARY 函数从组中返回任意值。当您知道组中所有记录的某列值相同,但该值不能标识该组时,该函数很有用。

优化前 N 个查询

ORDER BY 子句按排序顺序返回查询结果。Athena 使用分布式排序在多个节点上并行运行排序操作。

如果您不严格要求对结果进行排序,请避免添加 ORDER BY 子句。此外,如果不是严格必需的,请避免将 ORDER BY 添加到内部查询中。在许多情况下,查询计划程序可以删除冗余排序,但这并不能保证。此规则的一个例外情况是,如果内部查询正在执行前 N 个操作,例如查找 N 个最新或 N 个最常见的值。

当 Athena 看到 LIMITORDER BY 一起使用时,它会知道您正在运行前 N 个查询,因此会相应地使用专用操作。

注意

尽管 Athena 也可以经常使用前 N 个来检测 row_number 等窗口函数,但我们建议使用 ORDER BYLIMIT 的简单版本。有关更多信息,请参阅 优化窗口函数

仅包含必需列

如果您并不严格需要某一列,请不要将其包含在查询中。查询需要处理的数据越少,运行速度就越快。这样既可以减少所需的内存量,也减少了必须在节点之间发送的数据量。如果您使用的是列式文件格式,则减少列数也会减少从 Amazon S3 读取的数据量。

Athena 对结果中的列数没有具体限制,但是查询的执行方式限制了可能的列组合大小。列的组合大小包括其名称和类型。

例如,以下错误是由超出关系描述符大小限制的关系引起的:

GENERIC_INTERNAL_ERROR:io.airlift.bytecode。 CompilationException

要解决此问题,请减少查询中的列数,或创建子查询并使用可降低检索数据量的 JOIN。如果您的查询在最外层的查询中执行 SELECT *,则应将 * 更改为仅包含所需列的列表。

使用近似值优化查询

Athena 支持近似聚合函数,用于计算不同值、最常见的值、百分位数(包括近似中位数)和创建直方图。当不需要精确值时,请使用这些函数。

COUNT(DISTINCT col) 操作不同,approx_distinct 使用的内存要少得多,运行速度更快。同样,使用 numeric_histogram 代替直方图会使用近似法,因此内存更少。

优化 LIKE

您可以使用 LIKE 来查找匹配的字符串,但是对于长字符串,这将占用大量计算资源。在大多数情况下,regexp_like 函数是一种更快的替代方案,而且还提供了更大的灵活性。

通常,您可以通过锚定要查找的子字符串来优化搜索。例如,如果您正在寻找前缀,最好使用 'substr%' 而不是 '%substr%'。或者,如果您使用的是 regexp_like,请使用 '^substr'。

使用 UNION ALL 代替 UNION

UNION ALLUNION 还有两种方法可以将两个查询的结果合并为一个结果。UNION ALL 将第一个查询的记录与第二个查询的记录连接起来,UNION 执行相同的操作,同时也会删除重复的记录。UNION 需要处理所有记录并找到重复项,这需要占用大量内存和计算,但 UNION ALL 操作速度相对较快。除非需要对记录进行重复数据删除,否则请使用 UNION ALL 以获得最佳性能。

对大型结果集使用 UNLOAD

当查询的结果预计会很大(例如,成千上万行或更多)时,请使用 UNLOAD 导出结果。在大多数情况下,这比运行常规查询要快,而且使用 UNLOAD 还可以让您更好地控制输出。

查询执行完毕后,Athena 会将结果作为单个未压缩的 CSV 文件存储在 Amazon S3 上。这需要比 UNLOAD 更长的时间,这不仅是因为结果未压缩,还因为操作无法并行化。相比之下,UNLOAD 直接从 Worker 节点写入结果,并充分利用计算集群的并行度。此外,您可以配置 UNLOAD 为以压缩格式和其他文件格式(例如 JSON 和 Parquet)写入结果。

有关更多信息,请参阅 UNLOAD

使用 CTAS 或 Glue ETL 来具体化常用的聚合

‘Materializing' 查询是一种通过存储预先计算的复杂查询结果(例如聚合和联接),以便在后续查询中重复使用,从而提高查询性能的方法。

如果您的许多查询包含相同的联接和聚合,则可以将常见子查询具体化为新表,然后对该表运行查询。您可以使用 从查询结果创建表(CTAS) 或专用 ETL 工具(如 Glue ETL)创建新表。

例如,假设您有一个控制面板,其中包含显示订单数据集不同方面的小部件。每个小部件都有自己的查询,但所有查询都共享相同的联接和筛选器。订单表与订单项目表联接,并且有一个筛选器仅显示最近三个月。如果您确定了这些查询的常用功能,则可以创建小部件可以使用的新表。这样可以减少重复并提高性能。缺点是必须使新表保持最新状态。

重复使用查询结果

同一个查询通常会在短时间内多次运行。例如,当多人打开同一个数据控制面板时,可能会发生这种情况。运行查询时,您可以让 Athena 重复使用之前计算的结果。您可以指定要重复使用的结果的最大期限。如果之前在该时间范围内运行过相同的查询,Athena 将返回这些结果,而不会再次运行查询。有关更多信息,请参阅《Amazon Athena 用户指南》中的 重用查询结果,以及 Amazon 大数据博客中的通过 Amazon Athena 查询结果重复使用来降低成本并提高查询性能

查询优化技术

性能不仅取决于查询,还取决于数据集的组织方式及其使用的文件格式和压缩。

对您的数据进行分区

分区可将您的表格分成多个部分,并根据日期、国家或地区等属性将相关数据保存在一起。分区键充当虚拟列。您可以在创建表时定义分区键,并使用它们来筛选查询。对分区键列进行筛选时,只读取来自匹配分区的数据。例如,如果您的数据集按日期分区,并且您的查询的筛选条件仅与上周匹配,则只读取上周的数据。有关分区的更多信息,请参阅 在 Athena 中对数据进行分区

选择支持您的查询的分区键

由于分区会对查询性能产生重大影响,因此在设计数据集和表时,请务必考虑如何谨慎分区。分区键过多会导致数据集碎片化,文件过多或过小。相反,分区键太少或根本没有分区会导致查询扫描的数据超出必要的范围。

避免针对罕见的查询进行优化

一个好的策略是针对最常见的查询进行优化,避免针对罕见的查询进行优化。例如,如果您的查询以天为单位的时间跨度,即使某些查询筛选到该级别,也不要按小时进行分区。如果您的数据具有精细的时间戳列,则按小时筛选的罕见查询可以使用时间戳列。即使罕见案例扫描的数据比必要的要多一点,但为了极少数情况而降低整体性能通常也不是一个很好的权衡方案。

要减少查询必须扫描的数据量,从而提高性能,请使用列式文件格式并对记录进行排序。与其按小时分区,不如按时间戳对记录进行排序。对于时间范围较短的查询,按时间戳排序几乎与按小时分区一样有效。此外,按时间戳排序通常不会损害以天为单位的时间窗口的查询性能。有关更多信息,请参阅 使用列式文件格式

请注意,如果所有分区键上都有谓词,则对包含数万个分区的表进行查询的性能会更好。这是为最常见的查询设计分区方案的另一个原因。有关更多信息,请参阅 按等式查询分区

使用分区投影

分区投影是 Athena 的一项功能,它不是将分区信息存储在 Amazon Glue Data Catalog 中,而是作为规则存储在 Amazon Glue 中表的属性中。当 Athena 计划对配置了分区投影的表进行查询时,它会读取该表的分区投影规则。Athena 根据查询和规则计算要在内存中读取的分区,而不是在 Amazon Glue Data Catalog 中查找分区。

除了简化分区管理外,分区投影还可以提高具有大量分区的数据集的性能。当查询包含范围而不是分区键的特定值时,分区越多,在目录中查找匹配的分区所需的时间就越长。使用分区投影,无需进入目录即可在内存中计算筛选器,而且速度可以快得多。

在某些情况下,分区投影可能会导致性能降低。例如,当表为“稀疏表”时。稀疏表不包含分区投影配置所描述的分区键值的每个排列的数据。对于稀疏表,通过查询计算得出的分区集和分区投影配置都会在 Amazon S3 上列出,即使它们没有数据。

使用分区投影时,请确保在所有分区键上都包含谓词。缩小可能值的范围,以避免不必要的 Amazon S3 列表。假设一个分区键的范围为一百万个值,而一个查询对该分区键没有任何筛选器。要运行查询,Athena 必须执行至少一百万个 Amazon S3 列表操作。无论您是使用分区投影还是在目录中存储分区信息,查询特定值时查询速度最快。有关更多信息,请参阅 按等式查询分区

在为分区投影配置表时,请确保您指定的范围合理。如果查询不包含分区键的谓词,则使用该键范围内的所有值。如果您的数据集是在特定日期创建的,请使用该日期作为任何日期范围的起点。使用 NOW 作为日期范围的结束日期。避免使用具有大量值的数值范围,并考虑改用注入类型。

更多有关分区投影的信息,请参阅 使用 Amazon Athena 分区投影

使用分区索引

分区索引是 Amazon Glue Data Catalog 中的一项功能,它可以提高具有大量分区的表的分区查找性能。

目录中的分区列表就像关系数据库中的表。该表包含用于分区键的列,还有一列用于分区位置。查询分区表时,将通过扫描该表来查找分区位置。

与关系数据库一样,您可以通过添加索引来提高查询性能。您可以添加多个索引以支持不同的查询模式。Amazon Glue Data Catalog 分区索引同时支持等式和比较运算符,例如 >>=<,并与 AND 运算符组合使用。有关更多信息,请参阅《Amazon Glue 开发人员指南》中的在 Amazon Glue 中使用分区索引Amazon 大数据博客中的使用 Amazon Glue Data Catalog 分区索引提高 Amazon Athena 查询性能

始终使用 STRING 作为分区键的类型

在查询分区键时,请记住:Athena 要求分区键的类型必须为 STRING 类型才能下推分区筛选到 Amazon Glue。如果分区数量不小,则使用其他类型可能会导致性能降低。如果您的分区键值类似于日期或类似数字,请在查询中将其转换为相应的类型。

删除旧的和空的分区

如果您从 Amazon S3 上的分区中移除数据(例如,使用 Amazon S3 生命周期),则还应从 Amazon Glue Data Catalog 中删除该分区条目。在查询计划期间,与查询匹配的任何分区都会在 Amazon S3 上列出。如果您有许多空分区,则列出这些分区的开销可能会造成不利影响。

另外,如果您有成千上万个分区,可以考虑删除旧数据不再相关的分区元数据。例如,如果查询从不查看超过一年的数据,则可以定期删除旧分区的分区元数据。如果分区的数量增加到数万个,则移除未使用的分区可以加快所有分区键上都不包含谓词的查询速度。有关在查询中包含所有分区键的谓词的信息,请参阅 按等式查询分区

按等式查询分区

由于可以直接加载分区元数据,因此在所有分区键上包含等式谓词的查询运行得更快。避免查询中一个或多个分区键没有谓词,或者谓词选择一定范围的值。对于此类查询,必须筛选所有分区的列表以找到匹配的值。对于大多数表来说,开销很小,但是对于具有成千上万或更多分区的表,开销可能会变得很大。

如果无法重写查询以按等式筛选分区,则可以尝试分区投影。有关更多信息,请参阅 使用分区投影

避免使用 MSCK REPAIR TABLE 进行分区维护

由于 MSCK REPAIR TABLE 可能需要很长时间才能运行,只能添加新分区,而不会删除旧分区,因此这不是管理分区的有效方法(请参阅 注意事项和限制)。

使用 Amazon Glue Data Catalog APIALTER TABLE ADD PARTITIONAmazon Glue 爬网程序以更好地手动管理分区。作为替代方案,您可以使用分区投影,这样就无需管理分区。有关更多信息,请参阅 使用 Amazon Athena 分区投影

验证您的查询是否与分区方案兼容

您可以使用 EXPLAIN 语句提前检查查询将扫描哪些分区。在查询前面加上 EXPLAIN 关键字,然后在 EXPLAIN 输出底部附近查找每个表的源片段(例如 Fragment 2 [SOURCE])。查找右侧被定义为分区键的分配。下面的行包括运行查询时将扫描的该分区键的所有值的列表。

例如,假设您在表上有一个 dt 分区键的查询,并在查询前面加上 EXPLAIN。如果查询中的值是日期,并且筛选器选择了三天的范围,则 EXPLAIN 输出可能如下所示:

dt := dt:string:PARTITION_KEY :: [[2023-06-11], [2023-06-12], [2023-06-13]]

EXPLAIN 输出显示计划程序为该分区键找到了三个与查询相匹配的值。同时还会显示这些值是什么。有关使用 EXPLAIN 的更多信息,请参阅 在 Athena 中使用 EXPLAIN 和 EXPLAIN ANALYZE了解 Athena EXPLAIN 语句结果

使用列式文件格式

Parquet 和 ORC 等列式文件格式专为分布式分析工作负载而设计。它们按列而不是按行组织数据。以列式格式组织数据具有以下优点:

  • 仅加载查询所需的列

  • 减少了需要加载的总数据量

  • 列值存储在一起,因此可以有效地压缩数据

  • 文件可以包含允许引擎跳过加载不需要的数据的元数据

举例说明如何使用文件元数据,文件元数据可以包含有关数据页面中最小值和最大值的信息。如果查询的值不在元数据中注明的范围内,则可以跳过该页面。

使用此元数据来提高性能的一种方法是确保对文件中的数据进行排序。例如,假设您有查询来查找短时间段内 created_at 条目所在的记录。如果您的数据按 created_at 列排序,Athena 可以使用文件元数据中的最小值和最大值来跳过数据文件中不需要的部分。

使用列式文件格式时,请确保文件不要太小。如 避免文件过多 中所述,包含许多小文件的数据集会导致性能问题。对于列式文件格式尤其如此。对于小文件,列式文件格式的开销大于好处。

请注意,Parquet 和 ORC 在内部按行组(Parquet)和条带(ORC)进行组织。行组的默认大小为 128MB,条带的默认大小为 64MB。如果您有许多列,则可以增加行组和条带大小以提高性能。不建议将行组或条带大小减小到其默认值以下。

要将其他数据格式转换为 Parquet 或 ORC,您可以使用 Amazon Glue ETL 或 Athena。有关使用 Athena for ETL 的更多信息,请参阅 将 CTAS 和 INSERT INTO 用于 ETL 和数据分析

压缩数据

Athena 支持多种压缩格式。查询压缩数据更快,也更便宜,因为您需要为解压缩前扫描的字节数付费。

gzip 格式提供了良好的压缩比,并且在其他工具和服务中具有广泛的支持。zstd(Zstandard)格式是一种较新的压缩格式,在性能和压缩比之间取得了很好的平衡。

压缩 JSON 和 CSV 数据等文本文件时,请尽量在文件数量和文件大小之间取得平衡。大多数压缩格式都要求读取器从一开始就读取文件。这意味着通常不能并行处理压缩的文本文件。在查询处理过程中,大型未压缩文件通常在工作线程之间拆分,以实现更高的并行度;但是对于大多数压缩格式来说,这是不可能的。

避免文件过多 中所述,文件最好不要太多也不要太少。由于文件数量是可以处理查询的工作线程数量的限制,因此对于压缩的文件尤其如此。

有关在 Athena 中使用压缩的更多信息,请参阅 Athena 压缩支持

使用存储桶查找具有高基数的键

存储桶是一种根据其中一列的值将记录分发到单独文件中的技术。这样可以确保所有具有相同值的记录都位于同一个文件中。当您的键具有高基数并且您的许多查询都查找该键的特定值时,存储桶很有用。

例如,假设您查询一组记录寻求特定用户。如果数据按用户 ID 划分存储桶,Athena 会事先知道哪些文件包含特定 ID 的记录,哪些文件没有。这使得 Athena 能够只读取可能包含该 ID 的文件,从而大大减少了读取的数据量。它还缩短了在数据中搜索特定 ID 所需的计算时间。

存储桶的缺点

当查询经常在存储数据的列中搜索多个值时,存储桶的价值就会降低。查询的值越多,必须读取所有或大多数文件的可能性就越大。例如,如果您有三个存储桶,并且查询查找三个不同的值,则可能必须读取所有文件。当查询查找单个值时,存储桶效果最好。

有关更多信息,请参阅 在 Athena 中进行分区和分桶

避免文件过多

由许多小文件组成的数据集会导致整体查询性能不佳。当 Athena 计划查询时,它会列出所有分区位置,这需要时间。处理和请求每个文件也会产生计算开销。因此,从 Amazon S3 加载单个更大的文件要比从许多较小的文件加载相同记录要快。

在极端情况下,您可能会遇到 Amazon S3 服务限制。Amazon S3 每秒最多支持向单个索引分区发出 5500 个请求。最初,存储桶被视为单个索引分区,但是随着请求负载的增加,可以将其拆分为多个索引分区。

Amazon S3 着眼于请求模式,并根据键前缀进行拆分。如果您的数据集包含成千上万个文件,则来自 Athena 的请求可能会超过请求限额。即使文件较少,如果对同一个数据集进行多个并发查询,也可能超过限额。访问相同文件的其他应用程序可能会占请求总数。

超过请求速率 limit 时,Amazon S3 会返回以下错误。此错误包含在 Athena 中查询的状态信息中。

SlowDown: 请降低您的请求率

要排除故障,首先要确定错误是由单个查询引起的,还是由读取相同文件的多个查询引起的。如果是后者,请协调查询的运行,这样它们就不会同时运行。为此,请在应用程序中添加排队机制甚至重试。

如果运行单个查询触发错误,请尝试合并数据文件或修改查询以减少读取的文件。合并小文件的最佳时机是在写入它们之前。为此,请考虑以下技术:

  • 将写入文件的过程更改为写入更大的文件。例如,可以在写入记录之前将其缓冲更长时间。

  • 将文件放在 Amazon S3 上的某个位置,然后使用像 Glue ETL 这样的工具将它们合并成更大的文件。然后,将较大的文件移动到表格所指向的位置。有关更多信息,请参阅《Amazon Glue 开发人员指南》中的读取较大组中的输入文件Amazon re:Post 知识中心中的如何配置 Amazon Glue ETL 任务以输出较大文件?

  • 减少分区键的数量。当分区键过多时,每个分区可能只有几个记录,从而导致小文件数量过多。有关决定创建哪些分区的信息,请参阅 选择支持您的查询的分区键

避免在分区之外设置额外的存储层次结构

为避免查询计划开销,请在每个分区位置以扁平结构存储文件。请勿使用任何其他目录层次结构。

当 Athena 计划查询时,它会列出与该查询匹配的所有分区中的所有文件。尽管 Amazon S3 本身没有目录,但惯例是将 / 正斜杠解释为目录分隔符。当 Athena 列出分区位置时,它会递归列出找到的任何目录。当分区内的文件按层次结构组织时,会出现多轮列表。

当所有文件都直接位于分区位置时,大多数时候只需要执行一个列表操作。但是,如果分区中有超过 1000 个文件,则需要进行多次顺序列表操作,因为 Amazon S3 每次列表操作仅返回 1000 个对象。一个分区中有 1000 个以上的文件还会造成其他更严重的性能问题。有关更多信息,请参阅 避免文件过多

仅在必要时使用 SymlinkTextInputFormat

使用 SymlinkTextInputFormat 技术可以解决表的文件没有整齐地组织到分区中的情况。例如,当所有文件都使用相同前缀或具有不同架构的文件位于同一位置时,符号链接可能很有用。

但是,使用符号链接会增加查询执行的间接级别。这些间接级别会影响整体性能。必须读取符号链接文件,并且必须列出它们定义的位置。这会增加到 Amazon S3 的多次往返行程,而通常的 Hive 表不需要这些往返行程。总之,只有在没有更好的选项(例如重组文件)时,才应使用 SymlinkTextInputFormat

其他资源

有关 Athena 中性能优化的更多信息,请参考以下资源: