用于有效连接Spark数据帧/数据集的分区数据

时间:2018-01-09 02:22:26

标签: apache-spark apache-spark-sql spark-dataframe partitioning apache-spark-dataset

我需要根据一些共享键列将join多个DataFrame放在一起。对于键值RDD,可以指定分区器,以便将具有相同键的数据点混洗到同一个执行器,因此加入更有效(如果在join之前有一个shuffle相关操作)。可以在Spark DataFrames或DataSet上完成同样的事情吗?

2 个答案:

答案 0 :(得分:8)

如果你知道你将多次加入DataFrame,你可以repartition加载一个DataFrame

val users = spark.read.load("/path/to/users").repartition('userId)

val joined1 = users.join(addresses, "userId")
joined1.show() // <-- 1st shuffle for repartition

val joined2 = users.join(salary, "userId")
joined2.show() // <-- skips shuffle for users since it's already been repartitioned

因此,它会将数据混洗一次,然后在后续加入时重复使用随机播放文件。

但是,如果您知道您将反复对某些键上的数据进行洗牌,那么您最好的选择是将数据保存为分段表。这将把数据写入预先散列分区的数据,因此当您读取表并加入它们时,您可以避免随机播放。你可以这样做:

// you need to pick a number of buckets that makes sense for your data
users.bucketBy(50, "userId").saveAsTable("users")
addresses.bucketBy(50, "userId").saveAsTable("addresses")

val users = spark.read.table("users")
val addresses = spark.read.table("addresses")

val joined = newUsers.join(addresses, "userId")
joined.show() // <-- no shuffle since tables are co-partitioned data

为了避免混乱,桌子必须使用相同的桶(例如相同数量的桶并在桶列上连接)。

答案 1 :(得分:1)

可以使用repartition方法使用DataFrame / DataSet API。使用此方法,您可以指定一个或多个用于数据分区的列,例如

val df2 = df.repartition($"colA", $"colB")

同时也可以在同一命令中指定所需分区的数量,

val df2 = df.repartition(10, $"colA", $"colB")

注意:这并不能保证数据帧的分区位于同一节点上,只能保证分区以相同的方式完成。