优化Spark combineByKey

时间:2016-06-02 14:16:46

标签: apache-spark dataframe apache-spark-sql rdd shuffle

我正在尝试使用具有4.5 tb ram的群集处理大约2 tb的数据集。数据采用镶木地板格式,最初加载到数据框中。然后查询数据的子集并将其转换为RDD以进行更复杂的处理。该处理的第一个阶段是mapToPair使用每个行id作为元组中的键。然后,数据通过combineByKey操作以使用相同的键对所有值进行分组。此操作始终超出最大群集内存,并且作业最终会失败。虽然它正在洗牌,但是很多内存映射到磁盘上并且#34;消息。我想知道我是否最初将数据分区,以便所有具有相同id的行都驻留在同一分区中,如果它需要进行左移洗并正确执行。

要执行我正在使用的初始加载:

sqlContext.read().parquet(inputPathArray).repartition(10000, new Column("id"));

我不确定这是否是对数据帧进行分区的正确方法,因此我的第一个问题是上述问题。

我的下一个问题是当我从数据框转到rdd时使用:

JavaRDD<LocationRecord> locationsForSpecificKey = sqlc.sql("SELECT * FROM standardlocationrecords WHERE customerID = " + customerID + " AND partnerAppID = " + partnerAppID)
                    .toJavaRDD().map(new LocationRecordFromRow()::apply);

是保留的数据帧的分区方案,还是在使用mapToPair后需要重新分区:

rdd.partitionBy并传入一个使用ID字段哈希的自定义HashPartitioner。

我的目标是在执行最终的combineByKey时减少重排,以防止作业耗尽内存并失败。任何帮助将不胜感激。

谢谢, 森

1 个答案:

答案 0 :(得分:2)

  

我不确定这是否是分区数据框的正确方法

看起来是正确的。

  

是保留数据帧的分区方案

应保留数据分布,通过查看debugString

可以轻松查看
val df = sqlContext.read.parquet("/tmp/foo").repartition(10000, $"id")

df.rdd.toDebugString
// String =
// (10000) MapPartitionsRDD[46] at rdd at <console>:26 []
//    |    ShuffledRowRDD[45] at rdd at <console>:26 []
//    +-(8) MapPartitionsRDD[44] at rdd at <console>:26 []
//       |  $anon$1[43] at  []

但是没有为输出RDD设置分区器:

df.rdd.partitioner
// Option[org.apache.spark.Partitioner] = None

因此,此信息不能用于优化后续聚合。

  

我的目标是减少改组

如果是这样,它看起来不是一个正确的方法。假设传递给mergeValue的{​​{1}}函数是一个简化操作,那么实际上比直接应用combineByKey更有效。如果不是这种情况,那么将combineByKey设置为false combineByKey可能是更好的选择。

根据组合逻辑,您还应考虑直接在mapSideCombine上执行聚合。