我有两个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)
答案 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 []