写入s3时在Spark 2.1.0中设置spark.speculation

时间:2017-09-23 03:08:24

标签: apache-spark amazon-s3

我正在运行一个大型Spark 2.1.0,最后将结果写入s3。 它运行在30节点集群上,大部分工作正常。但是,偶尔我必须停止工作并再次运行它,因为即使在完成所有计算之后,单个节点也会在写入时卡住。我想知道我是否可以通过改变猜测来缓解这个问题。我在另一篇文章中读到这可能有害并导致重复结果或数据损坏。任何人都可以建议吗?我还建议通过在spark-defaults.conf中指定以下设置来使用hadoop默认提交者。我正在运行Spark standalone。

 spark.hadoop.mapreduce.fileoutputcommitter.algorithm.version 2

非常感谢对此问题的任何澄清。

1 个答案:

答案 0 :(得分:9)

更新: 如果您使用AWS Elastic MapReduce,版本为> = 5.19的群集现在可以安全地使用推测执行,但您的Spark作业仍然可以在中途失败并保留不完整的结果

如果您直接扫描AWS S3会导致下游作业的结果不正确,那么您的不完整结果中的部分数据是可查询的,因此您需要一个策略来处理它!

如果您正在运行Spark 2.3.0或更高版本,我建议您使用SaveMode.Overwrite将新分区写入确定性位置并在失败时重试,这样可以避免输出中出现重复或损坏的数据。

如果您使用SaveMode.Append,则重试Spark作业将在您的输出中生成重复数据。

推荐方法:

df.write
  .mode(SaveMode.Overwrite)
  .partitionBy("date")
  .parquet("s3://myBucket/path/to/table.parquet")

然后成功编写分区,将其原子注册到Hive等Metastore,并查询Hive作为您的真实来源,而不是直接查询。

例如

ALTER TABLE my_table ADD PARTITION (date='2019-01-01') location 's3://myBucket/path/to/table.parquet/date=2019-01-01'

如果您的Spark作业失败并且您正在使用SaveMode.Overwrite,那么重试总是安全的,因为数据尚未被Metastore查询使用,并且您只覆盖失败分区中的数据。 / p>

注意:为了仅覆盖特定分区而不是您需要配置的整个数据集:

spark.conf.set("spark.sql.sources.partitionOverwriteMode","dynamic")

仅可从Spark 2.3.0获得。

https://aws.amazon.com/blogs/big-data/improve-apache-spark-write-performance-on-apache-parquet-formats-with-the-emrfs-s3-optimized-committer/ https://docs.aws.amazon.com/emr/latest/ReleaseGuide/emr-spark-s3-optimized-committer.html

您可能还想将Iceberg项目视为Hive / Glue Metastore的替代品,因为它已经成熟。 https://github.com/apache/incubator-iceberg

有关为何需要此背景以及非AWS用户的背景

在提交到对象存储库时运行Spark猜测通常是非常坏主意,这取决于查看下游数据和一致性模型的内容。

来自Netflix的Ryan Blue有一个很好的(非常有趣的)演讲,它解释了原因:https://www.youtube.com/watch?v=BgHrff5yAQo

根据OP的描述判断,我怀疑他们正在写Parquet。

TL; dr版本是在S3中,重命名操作实际上是副本和删除,这具有一致性含义。通常在Spark中,输出数据将写入临时文件位置,并在计算完成时重命名。这意味着如果推测执行已启用,则多个执行程序可以处理相同的结果,然后通过将临时文件重命名为最终结果而首先完成“获胜”而另一个任务被取消。这个重命名操作发生在一个任务上,以确保只有一个推测任务获胜,这在HDFS上不是问题,因为重命名是一个廉价的元数据操作,几千或几百万只需要很少的时间。

但是当使用S3时,重命名不是原子操作,它实际上是需要时间的副本。因此,您可能会遇到这样的情况:您必须在S3中再次复制一大堆文件以进行重命名,这是一个同步操作,这会导致您的速度减慢。如果你的执行者有多个核心,你实际上可能有一个任务破坏了另一个核心的结果,这在理论上应该没问题,因为一个文件最终胜出,但是你无法控制那时发生的事情。

问题是,如果最终重命名任务失败会发生什么?您最终会将一些文件提交给S3而不是所有文件,这意味着部分/重复数据以及下游的许多问题,具体取决于您的应用程序。

虽然我不喜欢它,但目前流行的智慧是在本地写入HDFS,然后使用S3Distcp等工具上传数据。

看看HADOOP-13786。 Steve Loughran是这个问题的最佳人选。

如果您不想等待Ryan Blue有一个repo“rdblue / s3committer”,它允许您为除镶木地板文件之外的所有输出修复此问题,但看起来有点工作要正确地集成和子类化。

更新: HADOOP-13786现已修复并发布到Hadoop 3.1库中。 目前,Steven Loughran正在努力获得基于Hadoop 3.1的修复程序,并将其合并到apache / spark中,(SPARK-23977)然而根据票证评论线程的最新情况是修复程序在Spark 2.4发布之前不会合并,所以我们可能会等待一段时间才能成为主流。

更新v2: 注意:您可以通过在Hadoop配置中将mapreduce.fileoutputcommitter.algorithm.version设置为2来将最终输出分区重命名任务失败的时间窗口减半,因为原始输出提交机制实际执行 2 < / strong>重命名。