我正在尝试在非常大的数据集上使用Spark的 bucketBy 功能。
dataframe.write()
.format("parquet")
.bucketBy(500, bucketColumn1, bucketColumn2)
.mode(SaveMode.Overwrite)
.option("path", "s3://my-bucket")
.saveAsTable("my_table");
问题是我的Spark集群有大约500个分区/任务/执行程序(不确定术语),所以我最终得到的文件如下:
part-00001-{UUID}_00001.c000.snappy.parquet
part-00001-{UUID}_00002.c000.snappy.parquet
...
part-00001-{UUID}_00500.c000.snappy.parquet
part-00002-{UUID}_00001.c000.snappy.parquet
part-00002-{UUID}_00002.c000.snappy.parquet
...
part-00002-{UUID}_00500.c000.snappy.parquet
part-00500-{UUID}_00001.c000.snappy.parquet
part-00500-{UUID}_00002.c000.snappy.parquet
...
part-00500-{UUID}_00500.c000.snappy.parquet
这是500x500 = 250000个拼花实木复合地板文件! FileOutputCommitter
永远需要将其提交到S3。
是否可以像Hive中那样在每个存储桶中生成一个文件?还是有更好的方法来解决这个问题?到目前为止,似乎我不得不在降低群集的并行性(减少作者的数量)或降低木地板文件的并行性(减少存储桶的数量)之间进行选择。
谢谢
答案 0 :(得分:1)
为了每个最终存储桶获取1个文件,请执行以下操作。就在将数据帧作为表分区写入之前,它使用与用于存储分区的列完全相同的列,并将新分区的数量设置为等于您将在bucketBy中使用的存储分区的数量(或较小的数字,它是number的除数)桶,尽管我看不出在这里使用较小桶的原因。
在您的情况下,可能看起来像这样:
dataframe.repartition(500, bucketColumn1, bucketColumn2)
.write()
.format("parquet")
.bucketBy(500, bucketColumn1, bucketColumn2)
.mode(SaveMode.Overwrite)
.option("path", "s3://my-bucket")
.saveAsTable("my_table");
在保存到现有表的情况下,需要确保列的类型完全匹配(例如,如果列X在数据帧中为INT,但在表中的BIGINT则插入到重新分区中) X划分为500个存储桶将与X划分为BIGINT的X进行重新分区不匹配,最终您将有500个执行者再次写入500个文件。
只需100%清除-这种重新分区将在您的执行中增加另一步,即在1个执行程序上收集每个存储桶的数据(因此,如果以前未按相同的方式对数据进行分区,则将重新进行一次完整的数据重排)。我以为那正是您想要的。
在另一个答案的注释中也提到过,如果存储桶密钥倾斜,则需要为可能的问题做好准备。的确是这样,但是如果加载表之后的第一件事是在存储分区的同一列上进行聚合/联接,那么默认的Spark行为并不能为您提供很多帮助(对于选择按这些列进行存储)。取而代之的是,您将得到一个延迟的问题,并且仅在写入后尝试加载数据时才看到偏斜。
我认为,如果Spark提供一个设置来始终在写存储桶的表之前(尤其是在插入现有表时)对数据进行重新分区,那将是非常好的。
答案 1 :(得分:0)
这应该解决它。
dataframe.write()
.format("parquet")
.bucketBy(1, bucketColumn1, bucketColumn2)
.mode(SaveMode.Overwrite)
.option("path", "s3://my-bucket")
.saveAsTable("my_table");
将BucketBy函数的输入参数修改为1。 您可以从spark的git存储库-https://github.com/apache/spark/blob/f8d59572b014e5254b0c574b26e101c2e4157bdd/sql/core/src/main/scala/org/apache/spark/sql/DataFrameWriter.scala
中查看bucketBy的代码第一个拆分的部分00001,即部分00002基于保存存储桶表时运行的并行任务数。在您的情况下,您有500个并行任务正在运行。每个零件文件中的文件数取决于您为bucketBy函数提供的输入。
要了解有关Spark任务,分区,执行器的更多信息,请查看我的中型文章-https://medium.com/@tharun026