在Spark中保存具有非常大值的数据帧

时间:2018-05-15 18:06:28

标签: apache-spark pyspark apache-spark-sql

使用Spark数据框,我正在执行groupBy操作,以将与键相关联的所有值收集到列表中。收集值的大小可能有很大差异。事实上,我正在努力生成"文件"通过连接复合键的值进行后处理。

为了说明,df是一个包含3个字符串列A,B,C的数据框。

df.groupBy(concat($"A", lit("-"), $"B").alias("Key")).agg(collect_list($"C").alias("values"))

运行此查询以获取几行有效,这意味着该命令是正确的。

但是,当我尝试将完整输出保存为压缩CSV或Parquet时,此过程因多种原因而失败,包括内存问题(我试图调整)和Kryptoserilization。

我怀疑某些值非常大是问题所在。 这种情况有最好的做法吗?

1 个答案:

答案 0 :(得分:0)

虽然确切地知道你得到的错误会有所帮助,但问题很可能是由于过多的数据流入一行。要解决此问题,您可以使用随机列添加一些人工分区。这样,您的分组数据将在多行之间共享,然后在多个文件之间共享,从而防止发生OOM错误。您可以这样做:

val df = sc.parallelize(Seq((1, 2, 3), (1, 2, 4), (1, 2, 5), (1, 3, 2)))
    .toDF("A", "B", "C")

你要做的就是这个,C可能会变得太大。

df.groupBy("A", "B")
  .agg(collect_list('C))
  .show
+---+---+---------------+                                                       
|  A|  B|collect_list(C)|
+---+---+---------------+
|  1|  2|      [3, 4, 5]|
|  1|  3|            [2]|
+---+---+---------------+

相反,您可以添加随机列R以防止C变得太大。数据将按R的几个值进行拆分,从而减少可能在一行中可能存在的数据量。在这里,我为示例使用了3个可能的随机值,但在您的情况下应该需要更大的值。

val new_df = df.withColumn("R", floor(rand()*3)).groupBy("A", "B", "R").agg(collect_list('C) as "C").show
new_df.show
+---+---+---+------+
|  A|  B|  R|     C|
+---+---+---+------+
|  1|  2|  1|   [5]|
|  1|  2|  2|[3, 4]|
|  1|  3|  1|   [2]|
+---+---+---+------+

然后你可以像这样编写你的分区数据帧。

new_df.write.partitionBy("A", "B", "R").parquet("...")