限制数据帧分区的最大大小

时间:2018-08-27 16:59:47

标签: scala apache-spark apache-spark-sql

当我将数据帧写到csv时,将为每个分区创建一个.csv文件。假设我想将每个文件的最大大小限制为1 MB。我可以多次写入,并增加每次重新分区的参数。有没有一种方法可以提前计算要用于分区的参数,以确保每个文件的最大大小小于某些指定大小。

我想可能会有病理情况,所有数据最终都集中在一个分区上。因此,请采用较弱的假设,即我们仅要确保平均文件大小小于某个指定的数量(例如1 MB)。

2 个答案:

答案 0 :(得分:10)

1。单一数据框解决方案

我试图找出一个不会同时杀死集群的聪明主意,唯一想到的是:

  1. 计算序列化行的大小
  2. 获取否。 DataFrame中的行数
  3. 分区,除以预期大小
  4. 应该工作吗?

代码应如下所示:

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

再次,这只是起草的想法,没有关于您的数据的领域知识。

2。跨系统解决方案

通过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条记录,并估计大小并适用于所有行,如上例