Apache spark DataFrame partitionBy到单个拼花地板文件(每个分区)

Apache spark DataFrame partitionBy到单个拼花地板文件(每个分区),apache-spark,apache-spark-sql,Apache Spark,Apache Spark Sql,我想重新分区/合并我的数据,以便将其保存到每个分区的一个拼花文件中。我还想使用Spark SQL partitionBy API。所以我可以这样做: df.coalesce(1) .write .partitionBy("entity", "year", "month", "day", "status") .mode(SaveMode.Append) .parquet(s"$location") 我已经测试过了,它的性能似乎不太好。这是因为数据集中只有一个分区要

我想重新分区/合并我的数据,以便将其保存到每个分区的一个拼花文件中。我还想使用Spark SQL partitionBy API。所以我可以这样做:

df.coalesce(1)
    .write
    .partitionBy("entity", "year", "month", "day", "status")
    .mode(SaveMode.Append)
    .parquet(s"$location")
我已经测试过了,它的性能似乎不太好。这是因为数据集中只有一个分区要处理,所有文件的分区、压缩和保存都必须由一个CPU核心完成

在调用coalesce之前,我可以重写它来手动进行分区(例如使用具有不同分区值的过滤器)

但是使用标准的Spark SQL API有更好的方法吗?

顾名思义:

合并(numPartitions:Int):数据帧 返回一个新的DataFrame,该DataFrame正好包含numPartitions分区

您可以使用numPartitions参数来减少RDD/DataFrame中的分区数。它有助于在过滤大数据集后更高效地运行操作

关于您的代码,它的性能不好,因为您实际执行的是:

  • 将所有内容放在一个分区中,这会使驱动程序过载,因为它会将所有数据拉到驱动程序的一个分区中(这也是一个不好的做法)

  • coalesce
    实际上会洗牌网络上的所有数据,这也可能导致性能损失

  • shuffle是Spark的机制,用于重新分发数据,以便在分区之间对数据进行不同的分组。这通常涉及跨执行器和机器复制数据,这使得洗牌操作变得复杂且成本高昂

    洗牌概念对于管理和理解非常重要。最好是尽可能少地进行洗牌,因为这是一项昂贵的操作,因为它涉及磁盘I/O、数据序列化和网络I/O。为了组织洗牌的数据,Spark生成一组任务—映射任务来组织数据,并生成一组reduce任务来聚合数据。该术语来源于MapReduce,与Spark的map和reduce操作没有直接关系

    在内部,来自单个映射任务的结果会一直保存在内存中,直到无法匹配为止。然后,根据目标分区对它们进行排序并写入单个文件。在reduce端,任务读取相关的已排序块

    关于分割拼花地板,我建议您阅读关于使用拼花地板分割的Spark数据帧的答案,以及性能调整的Spark编程指南中的内容


    我希望这有帮助

    我遇到了完全相同的问题,我找到了一种方法,使用
    DataFrame.repartition()
    。使用
    coalesce(1)
    的问题是,并行度下降到1,最好的情况下速度很慢,最坏的情况下会出错。增加这个数字也无济于事——如果使用
    coalesce(10)
    可以获得更多的并行性,但最终每个分区会有10个文件

    要在不使用
    coalesce()
    的情况下为每个分区获取一个文件,请使用
    repartition()
    并使用您希望输出分区的列。因此,在您的情况下,请执行以下操作:

    import spark.implicits._
    df.repartition($"entity", $"year", $"month", $"day", $"status").write.partitionBy("entity", "year", "month", "day", "status").mode(SaveMode.Append).parquet(s"$location")
    
    一旦我这样做了,我会得到每个输出分区一个拼花文件,而不是多个文件


    我在Python中测试了这一点,但我认为在Scala中应该是相同的。

    您好,谢谢您的回复。我同意联合会有代价。在我当前的代码中,我手动对数据进行分区,然后在每个运行良好的分区上调用coalesce和save。但是我不想自己一步一步地编写分区,而是想使用正确的API。但在这样做时,联合必须先于分裂。这就是我被卡住的地方。但是你正在做的是把所有的东西都放在一个分区中,然后是分区比,你应该只是分区比,而不是分区比,但是我最终得到了很多文件。我想限制创建的拼花地板文件的数量。我每分钟传输并保存一次,所以每个分区已经有1440个文件。我不想让它成倍增加。好吧,让我这样说,您的代码将为每个分区向文件系统(本地或HDFS)写入一个拼花文件。这意味着,如果你有10个不同的实体和3个不同的年份,每个12个月,等等,你可能会创建1440个文件。我仍然不知道如何使用标准API有效地将数据保存到每个分区的一个拼花文件中,所以我不认为它回答了这个问题。你找到解决方案了吗?我猜@PatrickMcGloin没有报告,但这很有效,我会鼓励Patrick接受答案。@GlennieHellesSindholt-你是对的。答案被接受。感谢用户3033652。请注意,在scala 2.0中,需要提供一个新的org.apache.spark.sql.Column(“实体”)等作为参数repartition@morpheus或者干脆
    $“entity”
    。使用Spark 1.6,这非常适合拼花地板。然而,使用Avro,我仍然会在每个分区上有多个文件。