我有两个rdd,我想对rdd1的每个项目的RDD2项目进行一些计算。所以,我在用户定义的函数中传递RDD2,如下所示,但我得到的错误如rdd1 cannot be passed in another rdd
。如果我想在两个rdd上执行操作,我可以知道如何实现这个目标吗?
例如:
RDD1.map(line =>function(line,RDD2))
答案 0 :(得分:3)
Spark不支持嵌套RDD,因为错误说明了这一点。通常你必须通过重新设计算法来绕过它。
如何做到这取决于实际用例,function
中究竟发生了什么以及它的输出是什么。
有时候RDD1.cartesian(RDD2)
,每个元组执行操作然后按键减少都会有效。有时,如果您有(K,V)
类型,则两者之间的连接都可以正常工作。
如果RDD2很小,您可以随时在驱动程序中收集它,使其成为广播变量,并在function
而不是RDD2
中使用该变量。
@Edit:
例如,让我们假设您的RDD包含字符串,function
将计算RDD
中RDD2
的给定记录发生的次数:
def function(line: String, rdd: RDD[String]): (String, Int) = {
(line, rdd.filter(_ == line).count)
}
这将返回RDD[(String, Int)]
。
Idea1
您可以尝试使用RDD的cartesian
方法cartesian product。
val cartesianProduct = RDD1.cartesian(RDD2) // creates RDD[(String, String)]
.map( (r1,r2) => (r1, function2) ) // creates RDD[(String, Int)]
.reduceByKey( (c1,c2) => c1 + c2 ) // final RDD[(String, Int)]
此处function2
需要r1
和r2
(它们是字符串),如果相等则返回1
,如果不相等则返回0
。最终的地图会产生一个RDD
,它会有一个元组,其中的密钥是来自r1
的记录,而值将是总数。
问题1:如果你在RDD1
中有重复的字符串,这将不起作用。你必须考虑一下。如果RDD1
记录有一些完美的唯一ID。
问题2:这确实创造了很多对(对于两个RDD中的1mln记录,它会创建大约500bln对),会很慢并且很可能导致很多shuffling。
<强> Idea2 强>
我不理解你对RDD2大小lacs
的评论,所以这可能会或可能不会有效:
val rdd2array = sc.broadcast(RDD2.collect())
val result = RDD1.map(line => function(line, rdd2array))
问题:这可能会炸毁你的记忆。在collect()
上调用driver
,all
的{{1}}条记录将被加载到驱动程序节点的内存中。
Idea3
根据用例的不同,还有其他方法可以解决这个问题,例如brute force algorithm for Similarity Search与您的用例类似(不是意图)。其中一个替代解决方案是Locality Sensitive Hashing。