Scala 有没有办法重写Spark RDD distinct以使用mapPartitions而不是distinct?

Scala 有没有办法重写Spark RDD distinct以使用mapPartitions而不是distinct?,scala,apache-spark,distinct,shuffle,rdd,Scala,Apache Spark,Distinct,Shuffle,Rdd,我有一个RDD太大,无法在没有虚假错误的情况下一致执行不同的语句(例如SparkException阶段失败4次、ExecutorLostFailure、HDFS文件系统关闭、达到最大执行器失败次数、由于SparkContext关闭而取消阶段等) 我试图计算特定列中的不同ID,例如: print(myRDD.map(a => a._2._1._2).distinct.count()) 是否有一种简单、一致、不太频繁地执行上述命令的方法,可以使用mapPartitions、reduceByK

我有一个RDD太大,无法在没有虚假错误的情况下一致执行不同的语句(例如SparkException阶段失败4次、ExecutorLostFailure、HDFS文件系统关闭、达到最大执行器失败次数、由于SparkContext关闭而取消阶段等)

我试图计算特定列中的不同ID,例如:

print(myRDD.map(a => a._2._1._2).distinct.count())
是否有一种简单、一致、不太频繁地执行上述命令的方法,可以使用mapPartitions、reduceByKey、flatMap或其他使用比distinct更少洗牌的命令


另请参见

最好弄清楚是否存在另一个潜在问题,但以下内容将满足您的要求……这是一种较为全面的解决方法,但听起来似乎符合您的要求:

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

在我看来,这个问题有两种可能的解决方案:

  • 使用一个还原键
  • 使用映射分区
  • 让我们用一个例子来看看它们

    我有一个100.000电影评级的数据集,格式为(idUser,(idMovie,rating))。假设我们想知道有多少不同的用户对一部电影进行了评级:

    让我们首先使用distinct进行查看:

    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以比以前更有效的方式执行相同的操作

    最后,让我们使用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首先在数据集的每个分区中获得不同数量的用户,在不执行任何洗牌的情况下,将实例数从100000缩短到1709。然后,使用更少的数据量,就可以在整个RDD上执行一个不同的RDD,而不必担心混乱,也可以更快地获得结果


    我建议对mapPartitions而不是reduceByKey使用最后一个方案,因为它管理的数据量较低。另一个解决方案是使用这两个函数,首先是前面提到的mapPartitions,然后不是distinct,以前面提到的相同方式使用reduceByKey。

    从类似于
    rdd.mapPartitions(xs=>xs.toSet.ToInterator)的东西开始。
    可能是一个改进。这是类似的问题吗?另请参阅,我认为根本问题只是数据大小。。。我们有一个非常大的集群,有许多节点和一个纱线调度程序,distinct命令99%的时间都可以正常工作,但是在非常大的数据集上,我的程序崩溃了,错误消息非常模糊(请参见上面的问题帖子中的示例),因此我认为崩溃是由于过大的混乱造成的。我将试用这些解决方案,看看它们是否能以较低的洗牌次数缩短运行时间——谢谢!您的第二个示例似乎不起作用,因为aggregateByKey仅用于PAIRDDS,而不用于一般RDD。我试图将你的map函数更改为(a=>(a._2._1._2,1L)),但现在该行的其余部分不起作用。@GlennStrycker Oops,Fixed我正在使用以下函数进行测试:myRDD.map(a=>(a._2._1._2,a._2._1._2)).aggregateByKey(0L)((x,y)=>y.toLong,(x,y)=>x.toLong).keys.count还有一个想法:我已经使用了散列分区器在某些节点上放置了某些键。如果我对这个新键进行重新分区,aggregateByKey或ReduceeByKey是否能让我在分区内执行“独特”调用,但忽略任何随机移动?