我正在尝试读取一个包含许多小木地板文件的文件夹:600个文件,每个500KB。然后repartition
将它们分成2个文件。
val df = spark.read.parquet("folder")
df.repartition(2).write.mode("overwrite").parquet("output_folder")
这太慢了,最多10分钟。从spark UI,我可以看到2个执行程序正在处理2个任务。我给每个执行者10GB的内存。
那速度慢的原因是什么?是因为磁盘IO吗?以及在这种情况下如何提高性能。
编辑:我也尝试使用coalesce
,但效果似乎没有什么不同。
答案 0 :(得分:1)
第一个选择是在源代码级别上用小型镶木文件制作一个大文件,将它们合并为多个文件> 128 mb大小的文件或您想要的大小
how to merge multiple parquet files to single parquet file using linux or hdfs command?
第二个选项,即使用spark:读取小的Parquet文件,然后在使用Spark进行实际的数据业务处理逻辑之前,按照您的期望将它们写入相对较大的文件中(将性能因素考虑在内)。考虑)
第二个选项:
即使您不知道火花工作的配置是什么,我也不知道...但是总体上,coalesce
应该可以工作。对我有用。
在此示例中,我在src / main / resources下获取了小文件“ ./userdata*.parquet”(5个小文件,共110 KB)
并使用coalesce
...
方法: 将每个实木复合地板文件作为数据框读取,然后合并以制作单个数据框,然后coalesce
。
package com.examples
import org.apache.hadoop.conf._
import org.apache.hadoop.fs._
import org.apache.log4j.{Level, Logger}
import org.apache.spark.internal.Logging
import org.apache.spark.sql.{DataFrame, SaveMode, SparkSession}
import scala.collection.mutable
/** *
* take small pegs and make a large peg
* and coalesce it
*
* @author : Ram Ghadiyaram
*/
object ParquetPlay extends Logging {
Logger.getLogger("org").setLevel(Level.OFF)
//public FileStatus[] globStatus(Path pathPattern) throws IOException
def main(args: Array[String]): Unit = {
val appName = if (args.length >0) args(0) else this.getClass.getName
val spark: SparkSession = SparkSession.builder
.config("spark.master", "local")
.appName(appName)
.getOrCreate()
val fs = FileSystem.get(new Configuration())
val files = fs.globStatus(new Path("./userdata*.parquet")).map(_.getPath.toString)
val dfSeq = mutable.MutableList[DataFrame]()
println(dfSeq)
println(files.length)
files.foreach(x => println(x))
val newDFs = files.map(dir => {
dfSeq += spark.read.parquet(dir).toDF()
})
println(dfSeq.length)
val finalDF = dfSeq.reduce(_ union _)
.toDF
finalDF.show(false)
println(System.getProperty("java.io.tmpdir"))
println(System.getProperties.toString)
finalDF.coalesce(2)
.write
.mode(SaveMode.Overwrite)
.parquet(s"${System.getProperty("java.io.tmpdir")}/final.parquet")
println("done")
}
}
结果:大小几乎相等,如下所示,共有2个文件...在示例中再次生成了小文件,但是在您的情况下,由于文件大小为500KB,大约有600个文件,因此您可以看到文件的大小,并且可以决定{ {1}}(您期望的分区数)
第三种选择:正如评论中提到的 Minh (原始张贴者)...可能存在高度压缩的大文件,压缩后可能会变小这个。
答案 1 :(得分:0)
这是Spark当前具有的权衡(版本3.0应该可以解决),因为任务数应包含在1x1映射到文件数的映射中...因此,任务数越大,性能越好但从分区的角度来看确实不理想,因为在这种情况下文件可能很小。
另一个问题是,在大多数情况下,由于压缩算法不再具有有关键的信息,因此最终重新分区的数据集的数量将不断增长。对于现实生活中的大数据而言,这是一个主要问题,因为磁盘空间占用将大大增加。对于非常嵌套的数据集尤其如此。
一种解决方案是将数据集简化为简单模式,以便每次写入磁盘时都可以利用压缩算法。
希望有帮助!