我有两个RDD,使用以下代码:
val fileA = sc.textFile("fileA.txt")
val fileB = sc.textFile("fileB.txt")
然后我按键映射并减少它:
val countsB = fileB.flatMap(line => line.split("\n"))
.map(word => (word, 1))
.reduceByKey(_+_)
val countsA = fileA.flatMap(line => line.split("\n"))
.map(word => (word, 1))
.reduceByKey(_+_)
如果密钥存在于countA
中,我现在不想找到并删除countB中的所有密钥我尝试过类似的事情:
countsB.keys.foreach(b => {
if(countsB.collect().exists(_ == b)){
countsB.collect().drop(countsB.collect().indexOf(b))
}
})
但似乎它不会被密钥删除它们。
答案 0 :(得分:3)
您建议的代码有3个问题:
您正在collect
RDD,这意味着它们不再是RDD,它们作为普通的Scala集合被复制到驱动程序应用程序的内存中,因此您将失去Spark的并行性并冒着OutOfMemory错误的风险,以防您的数据集很大
在不可变的Scala集合(或drop
)上调用RDD
时,您不会更改原始集合,而是获得 new 集合那些记录丢失了,所以你不能指望原始集合改变
您无法访问传递给任何RDD高阶方法的函数中的RDD
(例如,在这种情况下为foreach
) - 传递给这些方法的任何函数都被序列化并发送对于工作者而言,RDD
是(故意)不可序列化的 - 将它们提取到驱动程序内存,序列化它们并发送回工作人员是没有意义的 - 数据已经分发给工作人员了!
要解决所有这些问题 - 当您想要使用一个RDD数据来转换/过滤另一个数据时,通常需要使用某种类型的 join
。在这种情况下,你可以这样做:
// left join, and keep only records for which there was NO match in countsA:
countsB.leftOuterJoin(countsA).collect { case (key, (valueB, None)) => (key, valueB) }
请注意,我在这里使用的collect
不是您使用的collect
- 这个PartialFunction
作为参数,行为类似map
的组合1}}和filter
,最重要的是:它不会将所有数据复制到驱动程序内存中。
编辑:正如The Archetypal Paul所评论的那样 - 你有一个更短更好的选择 - subtractByKey
:
countsB.subtractByKey(countsA)