我的RDD太大而不能一致地执行不同的语句而没有虚假错误(例如,SparkException阶段失败4次,ExecutorLostFailure,HDFS文件系统关闭,执行器失败的最大数量达到,因为SparkContext关闭而取消阶段,等等。)
我正在尝试计算特定列中的不同ID,例如:
print(myRDD.map(a => a._2._1._2).distinct.count())
是否有一种简单,一致,不太随机密集的方式来执行上述命令,可能使用mapPartitions,reduceByKey,flatMap或其他使用较少shuffle而不是不同的命令?
答案 0 :(得分:2)
最好弄清楚是否存在另一个潜在的问题,但下面会做你想做的事情......而不是围绕这样做,但听起来它会适合你的账单:
myRDD.map(a => (a._2._1._2, a._2._1._2))
.aggregateByKey(Set[YourType]())((agg, value) => agg + value, (agg1, agg2) => agg1 ++ agg2)
.keys
.count
或者甚至这似乎有效,但它不是联想和可交换的。它起作用的原因是Spark的内部工作原理......但我可能会错过一个案例......所以虽然更简单,但我不确定我是否相信它:
myRDD.map(a => (a._2._1._2, a._2._1._2))
.aggregateByKey(YourTypeDefault)((x,y)=>y, (x,y)=>x)
.keys.count
答案 1 :(得分:0)
我认为有2种可能的解决方案:
让我们用一个例子看一下它们。
我有一个100.000电影评分的数据集,格式为(idUser,(idMovie,rating))。假设我们想知道有多少不同的用户对电影进行了评分:
首先让我们使用与众不同的:
val numUsers = rddSplitted.keys.distinct()
println(s"numUsers is ${numUsers.count()}")
println("*******toDebugString of rddSplitted.keys.distinct*******")
println(numUsers.toDebugString)
我们将得到以下结果:
numUsers is 943
*******toDebugString of rddSplitted.keys.distinct*******
(2) MapPartitionsRDD[6] at distinct at MovieSimilaritiesRicImproved.scala:98 []
| ShuffledRDD[5] at distinct at MovieSimilaritiesRicImproved.scala:98 []
+-(2) MapPartitionsRDD[4] at distinct at MovieSimilaritiesRicImproved.scala:98 []
| MapPartitionsRDD[3] at keys at MovieSimilaritiesRicImproved.scala:98 []
| MapPartitionsRDD[2] at map at MovieSimilaritiesRicImproved.scala:94 []
| C:/spark/ricardoExercises/ml-100k/u.data MapPartitionsRDD[1] at textFile at MovieSimilaritiesRicImproved.scala:90 []
| C:/spark/ricardoExercises/ml-100k/u.data HadoopRDD[0] at textFile at MovieSimilaritiesRicImproved.scala:90 []
使用 toDebugString 函数,我们可以更好地分析RDD发生的情况。
现在,让我们使用 reduceByKey ,例如,计算每个用户投票的次数并同时获得不同用户的数量:
val numUsers2 = rddSplitted.map(x => (x._1, 1)).reduceByKey({case (a, b) => a })
println(s"numUsers is ${numUsers2.count()}")
println("*******toDebugString of rddSplitted.map(x => (x._1, 1)).reduceByKey(_+_)*******")
println(numUsers2.toDebugString)
我们现在将获得以下结果:
numUsers is 943
*******toDebugString of rddSplitted.map(x => (x._1, 1)).reduceByKey(_+_)*******
(2) ShuffledRDD[4] at reduceByKey at MovieSimilaritiesRicImproved.scala:104 []
+-(2) MapPartitionsRDD[3] at map at MovieSimilaritiesRicImproved.scala:104 []
| MapPartitionsRDD[2] at map at MovieSimilaritiesRicImproved.scala:94 []
| C:/spark/ricardoExercises/ml-100k/u.data MapPartitionsRDD[1] at textFile at MovieSimilaritiesRicImproved.scala:90 []
| C:/spark/ricardoExercises/ml-100k/u.data HadoopRDD[0] at textFile at MovieSimilaritiesRicImproved.scala:90 []
分析RDD的产生,我们可以看到 reduceByKey 以比以前的 distinct 更有效的方式执行相同的动作。
最后,让我们使用 mapPartitions 。主要目标是尝试首先区分数据集每个分区中的用户,然后获得最终的不同用户。
val a1 = rddSplitted.map(x => (x._1))
println(s"Number of elements in a1: ${a1.count}")
val a2 = a1.mapPartitions(x => x.toList.distinct.toIterator)
println(s"Number of elements in a2: ${a2.count}")
val a3 = a2.distinct()
println("There are "+ a3.count()+" different users")
println("*******toDebugString of map(x => (x._1)).mapPartitions(x => x.toList.distinct.toIterator).distinct *******")
println(a3.toDebugString)
我们将获得以下信息:
Number of elements in a1: 100000
Number of elements in a2: 1709
There are 943 different users
*******toDebugString of map(x => (x._1)).mapPartitions(x => x.toList.distinct.toIterator).distinct *******
(2) MapPartitionsRDD[7] at distinct at MovieSimilaritiesRicImproved.scala:124 []
| ShuffledRDD[6] at distinct at MovieSimilaritiesRicImproved.scala:124 []
+-(2) MapPartitionsRDD[5] at distinct at MovieSimilaritiesRicImproved.scala:124 []
| MapPartitionsRDD[4] at mapPartitions at MovieSimilaritiesRicImproved.scala:122 []
| MapPartitionsRDD[3] at map at MovieSimilaritiesRicImproved.scala:120 []
| MapPartitionsRDD[2] at map at MovieSimilaritiesRicImproved.scala:94 []
| C:/spark/ricardoExercises/ml-100k/u.data MapPartitionsRDD[1] at textFile at MovieSimilaritiesRicImproved.scala:90 []
| C:/spark/ricardoExercises/ml-100k/u.data HadoopRDD[0] at textFile at MovieSimilaritiesRicImproved.scala:90 []
我们现在可以看到, mapPartition 首先在数据集的每个分区中获得了不同的用户数,从而将实例数从100,000缩短到了1,709,而没有进行任何改组。然后,使用少得多的数据量,就可以在整个RDD上执行 distinct ,而不必担心数据洗牌,并且可以更快地获得结果。
我建议将最后一个建议与 mapPartitions 而不是 reduceByKey 一起使用,因为它管理的数据量较小。另一个解决方案可能是同时使用这两个函数,如前所述,先使用 mapPartitions ,然后再使用 reduceByKey 代替 distinct 以前。