是否可以加入两个rdds'值以避免昂贵的改组?

时间:2015-10-16 18:41:28

标签: scala join apache-spark

我有两个RDD,它们都有两列(K,V)。在这些RDD的源中,键出现在另一个下面,并且对于每一行,为键分配不同且不同的值。创建RDD的文本文件在本文的底部给出。

两个RDD中的密钥完全不同,我想根据它们的值加入两个RDD,并尝试查找每对的存在多少个公共值。例如我试图达到一个结果,如(1-5,10)意味着" 1"的关键值。来自RDD1和" 5"的关键值来自RDD2共享10个共同的值。

我在一台256 GB RAM和72个核心的机器上工作。一个文本文件是500 MB而另一个是3 MB。

这是我的代码:

val conf = new SparkConf().setAppName("app").setMaster("local[*]").set("spark.shuffle.spill", "true")
.set("spark.shuffle.memoryFraction", "0.4")
.set("spark.executor.memory","128g")
.set("spark.driver.maxResultSize", "0")

val RDD1 = sc.textFile("\\t1.txt",1000).map{line => val s = line.split("\t"); (s(0),s(1))}

val RDD2 = sc.textFile("\\t2.txt",1000).map{line => val s = line.split("\t"); (s(1),s(0))}


val emp_newBC = sc.broadcast(emp_new.groupByKey.collectAsMap)

        val joined = emp.mapPartitions(iter => for {
          (k, v1) <- iter
          v2 <- emp_newBC.value.getOrElse(v1, Iterable())
        } yield (s"$k-$v2", 1))

    joined.foreach(println)

val result = joined.reduceByKey((a,b) => a+b)

我尝试使用从脚本中看到的广播变量来管理此问题。如果我加入RDD2(有250K行),它们本身就会出现在同一个分区中,所以发生的洗牌次数减少所以需要3分钟才能得到结果。但是,当应用RDD1与RDD2时,这些对分散在分区中,导致非常昂贵的混洗过程,并且它总是最终给出

错误TaskSchedulerImpl:localhost上丢失的执行程序驱动程序:执行程序心跳在168591 ms错误后超时。

根据我的结果:

  • 我是否应该尝试对文本文件进行分区以在较小的块中创建RDD1 并使用RDD2分别加入那些较小的块?

  • 是否有其他方法可以根据其值字段连接两个RDD?如果我将原始值描述为键并将其与连接函数连接,则值对再次分散在分区上,这又导致非常昂贵的reducebykey操作。 e.g。

    val RDD1 = sc.textFile("\\t1.txt",1000).map{line => val s = line.split("\t"); (s(1),s(0))}
    
        val RDD2 = sc.textFile("\\t2.txt",1000).map{line => val s = line.split("\t"); (s(1),s(0))}
    

    RDD1.join(RDD2).map(line =&gt;(line._2,1))。reduceByKey((a,b)=&gt;(a + b))

PSEUDO DATA SAMPLE:

KEY VALUE
1   13894
1   17376
1   15688
1   22434
1   2282
1   14970
1   11549
1   26027
1   2895
1   15052
1   20815
2   9782
2   3393
2   11783
2   22737
2   12102
2   10947
2   24343
2   28620
2   2486
2   249
2   3271
2   30963
2   30532
2   2895
2   13894
2   874
2   2021
3   6720
3   3402
3   25894
3   1290
3   21395
3   21137
3   18739
...

一个小例子

RDD1集

2   1
2   2
2   3
2   4
2   5
2   6
3   1
3   6
3   7
3   8
3   9
4   3
4   4
4   5
4   6

RDD2

21  1
21  2
21  5
21  11
21  12
21  10
22  7
22  8
22  13
22  9
22  11

基于此数据加入结果:

(3-22,1)
(2-21,1)
(3-22,1)
(2-21,1)
(3-22,1)
(4-21,1)
(2-21,1)
(3-21,1)
(3-22,1)
(3-22,1)
(2-21,1)
(3-22,1)
(2-21,1)
(4-21,1)
(2-21,1)
(3-21,1)

REDUCEBYKEY结果:

(4-21,1)
(3-21,1)
(2-21,3)
(3-22,3)

1 个答案:

答案 0 :(得分:-1)

您是否考虑过使用笛卡尔加入?你可以试试下面的东西:

val rdd1 = sc.parallelize(for { x <- 1 to 3; y <- 1 to 5 } yield (x, y)) // sample RDD
val rdd2 = sc.parallelize(for { x <- 1 to 3; y <- 3 to 7 } yield (x, y)) // sample RDD with slightly displaced values from the first

val g1 = rdd1.groupByKey()
val g2 = rdd2.groupByKey()

val cart = g1.cartesian(g2).map { case ((key1, values1), (key2, values2)) => 
             ((key1, key2), (values1.toSet & values2.toSet).size) 
           }

当我尝试在群集中运行上面的示例时,我看到以下内容:

scala> rdd1.take(5).foreach(println)
...
(1,1)
(1,2)
(1,3)
(1,4)
(1,5)
scala> rdd2.take(5).foreach(println)
...
(1,3)
(1,4)
(1,5)
(1,6)
(1,7)
scala> cart.take(5).foreach(println)
...
((1,1),3)
((1,2),3)
((1,3),3)
((2,1),3)
((2,2),3)

结果表明,对于(key1,key2),集合之间有3个匹配元素。请注意,由于初始化的输入元组&#39;范围重叠3个元素。

笛卡尔变换不会引起混乱,因为它只是迭代每个RDD的元素并产生笛卡尔积。您可以通过调用示例上的toDebugString()函数来查看此内容。

scala> val carts = rdd1.cartesian(rdd2)
carts: org.apache.spark.rdd.RDD[((Int, Int), (Int, Int))] = CartesianRDD[9] at cartesian at <console>:25

scala> carts.toDebugString
res11: String =
(64) CartesianRDD[9] at cartesian at <console>:25 []
 |   ParallelCollectionRDD[1] at parallelize at <console>:21 []
 |   ParallelCollectionRDD[2] at parallelize at <console>:21 []