亚马逊AWS官方博客

从 Amazon EMR 和 AWS Glue 访问 Amazon S3 中数据的性能优化最佳实践

客户越来越多地构建数据湖,以在云中大规模存储数据。当您想要在数据湖中处理和分析数据时,通常会使用分布式计算引擎、云原生数据库和数据仓库。Amazon EMR 和 AWS Glue 是可用于此类使用场景的两项关键服务。Amazon EMR 是一个托管的大数据框架,支持多种不同的应用程序,包括 Apache SparkApache HivePrestoTrino 和 Apache HBase。AWS Glue Spark 作业在 Apache Spark 之上运行,并行分配数据处理工作负载用于执行提取、转换和加载(ETL, Extract, Transform, and Load)作业,从而大规模地扩充、去规范化、遮蔽和标记数据。

对于数据湖存储,客户通常会使用 Amazon Simple Storage Service(Amazon S3),因为它安全、可扩展、持久且具备高可用性。Amazon S3 专为满足 11 个 9 的持久性而设计,为全球数百万个应用程序存储超过 200 万亿个对象,使其成为数据湖的理想存储目标。Amazon S3 平均每秒执行超过 1 亿次操作,因此在将 Amazon S3 用作数据湖时,应用程序可以轻松实现高请求速率。

这篇文章介绍了使用 Amazon EMR 和 AWS Glue 分析 Amazon S3 中数据时,实现所需性能扩展的最佳实践。我们重点强调了在 Amazon EMR 和 AWS Glue Spark 作业上针对 Apache Spark 进行优化。

面向大型 Amazon EMR 和 AWS Glue 作业优化 Amazon S3 性能

Amazon S3 是一个非常大的分布式系统,在应用程序对 Amazon S3 读写数据时,您可以将请求性能扩展到每秒数千个事务。Amazon S3 性能不是按存储桶定义的,而是按存储桶中的前缀。在一个存储桶中,对于每个前缀,应用程序每秒可以处理至少 3500 个 PUT/COPY/POST/DELETE 请求或 5500 个 GET/HEAD 请求。此外,存储桶中的前缀数量没有限制,因此您可以利用并行处理,横向扩展读取或写入性能。例如,如果您在 S3 存储桶中创建 10 个前缀来并行执行读取操作,则可以将读取性能扩展到每秒 55000 个读取请求。同样,您也可以利用多个前缀写入数据来扩展写入性能。

您可以利用 Amazon S3 中的弹性伸缩功能来扩展性能,为运行在 PB 级数据上的查询扫描数百万个对象。Amazon S3 会自动扩展以响应持续的新请求速率,从而动态优化性能。在 Amazon S3 针对新的请求速率进行内部优化的时候,您会暂时收到 HTTP 503 请求响应,直至优化完成:

AmazonS3Exception: Please reduce your request rate.(Service: Amazon S3; Status Code: 503; Error Code: SlowDown)(AmazonS3Exception:请降低请求速率。(服务:Amazon S3;状态代码:503;错误代码:速度缓慢))

此类情况需要应用程序稍等片刻再重试,但在 Amazon S3 针对新请求速率完成内部性能优化后,所有请求通常无需重试即可得到处理。当分布式计算引擎(如 Amazon EMR 和 AWS Glue)中的多个工作线程临时生成大量的请求,需要访问相同前缀下的数据时,就会出现这种情况。

使用 Amazon EMR 和 AWS Glue 处理 Amazon S3 中的数据时,您可以采用某些最佳实践来管理请求流量,避免 HTTP 速度缓慢错误。我们来看看其中的一些策略。

管理 HTTP 速度缓慢响应的最佳实践

在使用 Amazon EMR 和 AWS Glue 访问 Amazon S3 数据时,您可以使用以下方法来利用 Amazon S3 中的横向扩展功能,提高请求的成功率:

  • 修改 Amazon S3 请求的重试策略
  • 调整处理的 Amazon S3 对象的数量
  • 调整并发 Amazon S3 请求的数量

我们建议您根据自己的使用场景选择并应用最适合的选项,以优化 Amazon S3 上的数据处理。在以下各节中,我们将介绍每种方法的最佳实践。

修改 Amazon S3 请求的重试策略

这是避免 HTTP 503 速度缓慢响应并提高请求成功率的最简单方法。要访问 Amazon S3 数据,Amazon EMR 和 AWS Glue 均使用 EMR 文件系统(EMRFS, EMR File System),该文件系统在收到 503 速度缓慢响应时,会以抖动方式重试 Amazon S3 请求。要提高 Amazon S3 请求的成功率,您可以通过配置某些属性来调整重试策略。在 Amazon EMR 中,您可以在 emrfs-site 配置中配置参数。在 AWS Glue 中,您可在作业参数中配置参数。您可通过以下方式调整重试策略:

  • 提高 EMRFS 原定设置重试次数限制 – 默认情况下,EMRFS 使用指数回退策略来重试向 Amazon S3 发出的请求。原定设置 EMRFS 重试限制为 15 次。不过,当您在创建新机群时、在运行中的集群上时或对于应用程序运行时,可以提高此限制。要提高重试次数限制,可以更改 fs.s3.maxRetries 参数的值。请注意,如果为此参数设置较高的值,则可能会出现较长的作业持续时间。我们建议使用不同的值进行试验,例如以 20 作为起点,确认每个值的作业持续时间开销,然后根据您的要求调整此参数。
  • 为 Amazon EMR 使用 AIMD 重试策略 – 对于 Amazon EMR 6.4.0 及更高版本,EMRFS 支持基于和式增加/积式减少(AIMD, Additive-Increase/Multiplicative-Decrease)模型的替代重试策略。此策略在调整来自大型集群的请求速率时非常有用。此模式不会单独处理每个请求,而是跟踪最近的成功请求速率和受限制请求速率。根据最近的成功请求速率来确定请求的限制速率。这减少了受限制请求的数量,从而减少了每个请求所需的尝试次数。要启用 AIMD 重试策略,可以将 fs.s3.aimd.enabled 属性设置为 true。您可以使用高级 AIMD 重试设置进一步优化 AIMD 重试策略。

调整处理的 Amazon S3 对象的数量

另一种方法是调整已处理的 Amazon S3 对象的数量,以便减少同时发出的请求数。当您减少作业中要处理的对象数量时,就会减少使用的 Amazon S3 请求数,从而降低每个作业所需的请求速率或每秒事务数(TPS, Transactions Per Second)。请注意以下注意事项:

  • 通过将多个较小的文件聚合为较少数量的较大数据块来预处理数据 – 例如,使用 s3-dist-cp 或 AWS Glue 压缩蓝图,将大量小文件(通常小于 64 MB)合并为数量更少的优化大小文件(例如 128–512 MB)。这种方法减少了所需的请求数量,同时提高了 Amazon S3 中读取和处理数据的总吞吐量。您可能需要进行实验,以获得适合您工作负载的最优大小,因为创建极大的文件会降低作业的并行度。
  • 使用分区修剪功能扫描特定分区下的数据 – 在与 Apache Hive 和 Hive MetaStore 兼容的应用程序(如 Apache Spark 或 Presto)中,一个表可以有多个分区文件夹。分区修剪是一种仅在表的特定分区文件夹中扫描所需数据的技术。此技术在需要从整个表中读取特定部分时非常有用。要利用谓词下推,您可以在 Spark SQL 的 WHERE 子句中使用分区列,或者在 DataFrame 中使用筛选表达式。在 AWS Glue 中,您还可以在创建 DynamicFrames 时使用分区下推谓词
  • 对于 AWS Glue,启用作业书签 – 您可以使用 AWS Glue 作业书签来重复处理连续摄入的数据。它仅从上一次作业运行中选取未处理的数据,从而减少了 Amazon S3 读取或检索的对象数量。
  • 对于 AWS Glue,启用边界执行 – AWS Glue 边界执行是一种仅选取未处理数据的技术,其上限为要处理的数据集大小或文件数。这是另一种减少向 Amazon S3 发出的请求数量的方法。

调整并发 Amazon S3 请求的数量

要调整 Amazon S3 请求的数量以减少各个前缀的并发读取数,您可以配置 Spark 参数。默认情况下,在创建 Spark DataFrame 时,Spark 会填充 10000 个任务以列出前缀。您可能会遇到速度缓慢响应,尤其是在读取具有高度嵌套的前缀结构的表时。在这种情况下,最好配置 Spark,通过减小参数 spark.sql.sources.parallelPartitionDiscovery.parallelism(原定设置值为 10000)来限制最大列表并行度的数量。

要减少每个前缀的并发写入请求数,可以使用以下技巧:

  • 在写入之前减少 Spark RDD 分区的数量 – 您可在 DataFrames 中使用 df.repartition(n) 或 df.coalesce(n) 做到这一点。对于 Spark SQL,您还可以使用 REPARTITION 或 COALESCE 等查询提示。您可以在 Spark UI 上看到任务数量(= RDD 分区数)
  • 对于 AWS Glue,分组输入数据 – 如果数据集由小文件组成,我们建议对输入数据进行分组,因为这样可以减少 RDD 分区的数量并减少写入文件的 Amazon S3 请求数量。
  • 使用 EMRFS S3 优化的提交程序 – 默认情况下,Amazon EMR 5.19.0 及更高版本以及 AWS Glue 3.0 中使用 EMRFS S3 优化的提交程序。在 AWS Glue 2.0 中,您可以在作业参数 --enable-s3-parquet-optimized-committer 中配置此项。提交程序使用 Amazon S3 分段上传而不是重命名文件,并且这样通常可显著减少 HEAD/LIST 请求数。

以下介绍在 Amazon EMR 和 AWS Glue 中调整 Amazon S3 请求速率的其他技巧。这些选项的纯效果是减少 Spark 作业的并行度,从而减少出现 Amazon S3 速度缓慢响应的可能性,尽管这可能导致较长的作业持续时间。我们建议针对使用场景测试和调整这些值。

  • 减少并发作业的数量 – 从最繁重的读/写作业开始。如果您为 Amazon S3 配置了跨账户访问,请记住,其他账户也可能向该前缀提交作业。
  • 减少并发 Spark 任务的数量 – 您有以下几种选择:
    • 对于 Amazon EMR,请设置 Spark 执行程序的数量(例如,spark-submit 选项 --num-executors 和 Spark 参数 spark.executor.instance)。
    • 对于 AWS Glue,请在 NumberOfWorkers 参数中设置工作线程的数量。
    • 对于 AWS Glue,请将 WorkerType 参数更改为较小的值(例如,从 G.2X 更改为 G.1X)。
    • 配置 Spark 参数:
      • 减少 spark.default.parallelism 的数量。
      • 减少 spark.sql.shuffle.partitions 的数量。
      • 增加 spark.task.cpus 的数量(原定设置为 1),为每个 Spark 任务分配更多 CPU 核心数。

结论

在这篇文章中,我们针对从 Amazon EMR 和 AWS Glue 访问 Amazon S3 中的数据,介绍了优化访问性能的最佳实践。借助这些最佳实践,您可以利用 Amazon S3 横向扩展轻松运行 Amazon EMR 和 AWS Glue 作业,并以高度分布的方式大规模处理数据。

如需更多指导,请联系 AWS Premium Support

附录 A:配置 CloudWatch 请求指标

要监控 Amazon S3 请求,您可以在 Amazon CloudWatch 中为存储桶启用请求指标。然后,为前缀定义一个筛选条件。有关要监控的有用指标列表,请参阅使用 Amazon CloudWatch 监控指标。启用指标后,使用指标中的数据来确定上述哪个选项最适合您的使用场景。

附录 B:配置 Spark 参数

在 Amazon EMR 中配置 Spark 参数有几个选项:

  • spark-submit command – 您可以通过 --conf 选项传递 Spark 参数。
  • 作业脚本 – 您可以在作业脚本代码的 SparkConf 对象中设置 Spark 参数。
  • Amazon EMR 配置 – 您可以使用 Amazon EMR 配置,通过 API 配置 Spark 参数。有关更多信息,请参阅配置 Spark

要在 AWS Glue 中配置 Spark 参数,您可以使用键 --conf 及类似于 spark.hadoop.fs.s3.maxRetries=50 的值来配置 AWS Glue 作业参数。

要设置多个配置,您可以使用键 --conf 及类似于 spark.hadoop.fs.s3.maxRetries=50 --conf spark.task.cpus=2 的值来配置作业参数。

本篇作者

Noritaka Sekiyama

Noritaka Sekiyama 是 AWS Glue 团队的首席大数据架构师。他热衷于发布 AWS Glue 连接器自定义蓝图和其他软件构件,帮助客户构建数据湖。在业余时间,他喜欢和孩子们一起观察寄居蟹。

Aditya Kalyanakrishnan

Aditya Kalyanakrishnan 是 AWS 的 Amazon S3 团队的高级产品经理。他乐于从客户那儿了解如何使用 Amazon S3 并帮助他们扩展性能。Adi 住在西雅图,业余时间喜欢徒步旅行,以及偶尔酿啤酒。