Join 如何在spark中有效地将大型rdd连接到非常大型的rdd?

Join 如何在spark中有效地将大型rdd连接到非常大型的rdd?,join,apache-spark,rdd,Join,Apache Spark,Rdd,我有两个RDD。一个RDD在500-1000万个条目之间,另一个RDD在5-7.5亿个条目之间。在某种程度上,我必须使用一个公共密钥连接这两个RDD val rddA = someData.rdd.map { x => (x.key, x); } // 10-million val rddB = someData.rdd.map { y => (y.key, y); } // 600-million var joinRDD = rddA.join(rddB); 当spark决定执行

我有两个RDD。一个RDD在500-1000万个条目之间,另一个RDD在5-7.5亿个条目之间。在某种程度上,我必须使用一个公共密钥连接这两个RDD

val rddA = someData.rdd.map { x => (x.key, x); } // 10-million
val rddB = someData.rdd.map { y => (y.key, y); } // 600-million
var joinRDD = rddA.join(rddB);
当spark决定执行此连接时,它决定执行ShuffledHashJoin。这会导致rddB中的许多项在网络上被洗牌。同样,一些rddA也在网络上被洗牌。在这种情况下,rddA太“大”了,无法用作广播变量,但看起来BroadcastHashJoin更有效。有没有提示spark使用BroadcastHashJoin?(ApacheFlink通过连接提示支持这一点)

如果没有,是否是提高自动驾驶阈值的唯一选项

2014年7月更新

我的性能问题似乎完全源于重新划分。通常,从HDFS读取的RDD将按块进行分区,但在本例中,源是一个拼花地板数据源[我制作的]。当spark(databricks)写入拼花地板文件时,它会为每个分区写入一个文件,同样地,它会为每个文件读取一个分区。所以,我找到的最佳答案是,在生成数据源的过程中,要按键对其进行分区,请写出拼花地板接收器(然后自然地进行共分区),并将其用作rddB


给出的答案是正确的,但我认为有关拼花地板数据源的详细信息可能对其他人有用。

您可以使用相同的分区器对RDD进行分区,在这种情况下,具有相同密钥的分区将并置在同一执行器上

在这种情况下,您将避免为联接操作洗牌

洗牌只会发生一次,当您要更新parititoner时,如果您要缓存RDD的所有连接,那么之后的所有连接都应该是执行者本地的

import org.apache.spark.SparkContext._

class A
class B

val rddA: RDD[(String, A)] = ???
val rddB: RDD[(String, B)] = ???

val partitioner = new HashPartitioner(1000)

rddA.partitionBy(partitioner).cache()
rddB.partitionBy(partitioner).cache()
您还可以尝试更新广播阈值大小,也许rddA可以广播:

--conf spark.sql.autoBroadcastJoinThreshold=300000000 # ~300 mb

我们使用400mb进行广播连接,效果很好。

我担心你会这么说。我已经试过使用partitionBy,而你基本上很早就受到了惩罚。不幸的是,我从RDD上游“读取”了一个文件,实际上没有一种好方法可以直接读取到分区结构中,所以我必须在读取之后进行分区。我曾经玩过autoBroadcastJoinThreshold——所以我知道它是有效的,我只是不想玩。正如我在OP中所说,这是Flink提供控制的一个领域,我希望Spark能够做到。感谢您的回复。我知道--conf spark.sql.autoBroadcastJoinThreshold只适用于数据帧或数据集之间的连接(spark-sql)。它也用于RDD联接吗?谢谢