Apache spark Spark Streaming以拼花格式附加到S3,小分区太多

Apache spark Spark Streaming以拼花格式附加到S3,小分区太多,apache-spark,amazon-s3,streaming,parquet,Apache Spark,Amazon S3,Streaming,Parquet,我正在构建一个应用程序,它使用Spark Streaming从AWS EMR上的Kinesis streams接收数据。目标之一是将数据持久化到S3 EMRFS中,为此,我使用了一个2分钟的非重叠窗口 我的做法: Kinesis Stream->Spark Streaming,批处理持续时间约60秒,使用120秒的非重叠窗口,将流数据保存到S3中,如下所示: val rdd1 = kinesisStream.map( rdd => /* decode the data */) rdd1.w

我正在构建一个应用程序,它使用Spark Streaming从AWS EMR上的Kinesis streams接收数据。目标之一是将数据持久化到S3 EMRFS中,为此,我使用了一个2分钟的非重叠窗口

我的做法:

Kinesis Stream->Spark Streaming,批处理持续时间约60秒,使用120秒的非重叠窗口,将流数据保存到S3中,如下所示:

val rdd1 = kinesisStream.map( rdd => /* decode the data */)
rdd1.window(Seconds(120), Seconds(120).foreachRDD { rdd =>
        val spark = SparkSession...
        import spark.implicits._
        // convert rdd to df
        val df = rdd.toDF(columnNames: _*)
        df.write.parquet("s3://bucket/20161211.parquet")
}
下面是s3://bucket/20161211.parquet经过一段时间后的样子:

正如您所看到的,很多零碎的小分区对读取性能来说非常糟糕……问题是,在我将数据流传输到这个S3拼花地板文件时,有没有办法控制小分区的数量

谢谢

我想做的是每天做这样的事情:

val df = spark.read.parquet("s3://bucket/20161211.parquet")
df.coalesce(4).write.parquet("s3://bucket/20161211_4parition.parquet")
在这里,我将数据帧重新划分为4个分区,并将它们保存回来


它可以工作,我觉得每天这样做并不是一个优雅的解决方案…

这实际上非常接近您想要做的事情,每个分区都将在Spark中作为一个单独的文件写入。然而,coalesce有点混乱,因为它可以有效地应用于调用coalesce的位置的上游。Scala文档中的警告是:

在Dataset中,持久化和计数更容易进行广泛的计算,因为默认的coalesce函数不将重新分区作为输入的标志,尽管您可以手动构造重新分区的实例


另一种选择是使用第二个定期批处理作业,甚至是第二个清理/合并结果的流式作业,但这可能有点复杂,因为它引入了第二个要跟踪的移动部件。

感谢Holden的回答!我喜欢选择第二个计划的批处理作业,这样做感觉更干净。我们有一个调度程序服务,所以这应该是可管理的。您有一个平面数据模式吗?或者如何确保每个拼花文件的模式保持不变?@V.Samma Spark DF parquet read有一个选项mergeSchema default为false,可以帮助您管理不断演变的模式?是的,但它不能处理同一列具有不同类型的情况。例如,一列应为双精度类型,一个数据行的值为2.0,另一个数据行的值为0。我不知道它是如何处理Spark流的,但是当Spark的read.json读取一个文件,其中该列中的所有值都是0时,它就会推断出它的类型,只要不加倍,并且在写入parquet之后,mergeSchema引发异常,因为它不知道如何处理这种情况。@V.Samma确实,具有不同类型的相同列名无法合并。我想不出一个好办法来处理这件事。我的想法是,在系统稳定后,不要直接更改类型,而是添加一个具有不同类型的新列。
However, if you're doing a drastic coalesce, e.g. to numPartitions = 1,
this may result in your computation taking place on fewer nodes than
you like (e.g. one node in the case of numPartitions = 1). To avoid this,
you can pass shuffle = true. This will add a shuffle step, but means the
current upstream partitions will be executed in parallel (per whatever
the current partitioning is).