我正在审查一些同事编写的代码,我找到了这样的方法:
def writeFile(df: DataFrame,
partitionCols: List[String],
writePath: String): Unit {
val df2 = df.repartition(partitionCols.get.map(col): _*)
val dfWriter = df2.write.partitionBy(partitionCols.get.map(col): _*)
dfWriter
.format("parquet")
.mode(SaveMode.Overwrite)
.option("compression", "snappy")
.save(writePath)
}
通常最好在预定义的一组列上调用repartition
,然后调用partitionBy
,然后保存到磁盘?
答案 0 :(得分:1)
通常,您使用与repartition
相同的列调用partitionBy
,以在每个分区中包含一个镶木地板文件。这是在这里实现的。现在你可以争辩说这可能意味着镶木地板文件大小变大或者更糟糕可能导致内存溢出。
此问题通常通过向Dataframe添加row_number来处理,然后指定文档的数量,而不是每个镶木地板文件可以具有的数量。像
这样的东西val repartitionExpression =colNames.map(col) :+ floor(col(RowNumber) / docsPerPartition)
// now use this to repartition
要将下一部分作为persist after partitionBy
回答,此处不需要分区后将其直接写入磁盘。
答案 1 :(得分:0)
为了帮助您理解partitionBy()
和repartition()
之间的差异,对数据框的重新分区使用基于哈希的分区程序,该分区程序采用COL以及基于NumOfPartitions的NumOfPartitions,生成哈希值并存储数据。
默认情况下,repartition()
会创建200个分区。由于可能发生冲突,很有可能将具有不同密钥的多个记录划分到相同的桶中。
另一方面,partitionBy()
采用COL,分区完全基于唯一键。分区与数据中唯一键的no:成比例。
在重新分区的情况下,很有可能编写空文件。但是,在partitionBy的情况下,不会有任何空文件。
答案 2 :(得分:0)
你的工作是CPU绑定的,内存绑定的,网络IO绑定的,还是磁盘IO绑定的?
如果df2足够大,前2个案例是重要的,其他答案正确地解决了这些案例。
如果您的工作受到磁盘IO约束(并且您认为自己将来经常将大文件写入HDFS),许多云提供商将允许您选择更快的SSD磁盘,但需额外付费。
同样Sandy Ryza建议将--executor-cores保持在5:
之下我注意到HDFS客户端遇到了大量并发线程的问题。粗略的猜测是每个执行程序最多可以有五个任务可以实现完全写入吞吐量,因此最好将每个执行程序的核心数保持在该数量之下。