使用Spark数据框,我正在执行groupBy操作,以将与键相关联的所有值收集到列表中。收集值的大小可能有很大差异。事实上,我正在努力生成"文件"通过连接复合键的值进行后处理。
为了说明,df是一个包含3个字符串列A,B,C的数据框。
df.groupBy(concat($"A", lit("-"), $"B").alias("Key")).agg(collect_list($"C").alias("values"))
运行此查询以获取几行有效,这意味着该命令是正确的。
但是,当我尝试将完整输出保存为压缩CSV或Parquet时,此过程因多种原因而失败,包括内存问题(我试图调整)和Kryptoserilization。
我怀疑某些值非常大是问题所在。 这种情况有最好的做法吗?
答案 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("...")