当我将数据帧写到csv时,将为每个分区创建一个.csv文件。假设我想将每个文件的最大大小限制为1 MB。我可以多次写入,并增加每次重新分区的参数。有没有一种方法可以提前计算要用于分区的参数,以确保每个文件的最大大小小于某些指定大小。
我想可能会有病理情况,所有数据最终都集中在一个分区上。因此,请采用较弱的假设,即我们仅要确保平均文件大小小于某个指定的数量(例如1 MB)。
答案 0 :(得分:10)
我试图找出一个不会同时杀死集群的聪明主意,唯一想到的是:
代码应如下所示:
val df: DataFrame = ??? // your df
val rowSize = getBytes(df.head)
val rowCount = df.count()
val partitionSize = 1000000 // million bytes in MB?
val noPartitions: Int = (rowSize * rowCount / partitionSize).toInt
df.repartition(noPartitions).write.format(...) // save to csv
// just helper function from https://stackoverflow.com/a/39371571/1549135
def getBytes(value: Any): Long = {
val stream: ByteArrayOutputStream = new ByteArrayOutputStream()
val oos = new ObjectOutputStream(stream)
oos.writeObject(value)
oos.close
stream.toByteArray.length
}
虽然我的第一选择是计算每一行的字节大小,但效率极低。因此,除非每行中的数据大小差异很大,否则我会说此解决方案将起作用。您还可以计算每第n行的大小。你明白了。
此外,我只是“希望” Long
足够大以支持预期的大小来计算noPartitions
。如果没有(如果您有很多行),也许最好更改操作顺序,例如:
val noPartitions: Int = (rowSize / partitionSize * rowCount).toInt
再次,这只是起草的想法,没有关于您的数据的领域知识。
通过apache-spark docs时,我发现了一个有趣的跨系统解决方案:
spark.sql.files.maxPartitionBytes
设置为:
读取文件时打包到单个分区中的最大字节数。
默认值为134217728 (128 MB)
。
所以我想您可以将其设置为1000000 (1MB)
,它将对您的DataFrames
产生永久影响。 但是,分区大小过小可能会严重影响您的性能!
您可以在SparkSession
创建过程中进行设置:
val spark = SparkSession
.builder()
.appName("Spark SQL basic example")
.config("spark.sql.files.maxPartitionBytes", 100000)
.getOrCreate()
以上所有条件仅在(我没记错并且)csv分区的文件数与DataFrame的分区数相同时有效。
答案 1 :(得分:1)
val df = spark.range(10000000)
df.cache
val catalyst_plan = df.queryExecution.logical
val df_size_in_bytes = spark.sessionState.executePlan(catalyst_plan).optimizedPlan.stats.sizeInBytes
df_size_in_bytes:BigInt = 80000000
最好的解决方案是获取100条记录,并估计大小并适用于所有行,如上例