排序后的数据帧分区数?

时间:2018-12-14 19:58:17

标签: apache-spark apache-spark-sql

在使用orderBy之后,spark如何确定分区数?我一直以为所得到的数据帧具有spark.sql.shuffle.partitions,但事实并非如此:

val df = (1 to 10000).map(i => ("a",i)).toDF("n","i").repartition(10).cache

df.orderBy($"i").rdd.getNumPartitions // = 200 (=spark.sql.shuffle.partitions)
df.orderBy($"n").rdd.getNumPartitions // = 2 

在两种情况下,spark都+- Exchange rangepartitioning(i/n ASC NULLS FIRST, 200)起作用,那么第二种情况下的结果分区数如何为2?

2 个答案:

答案 0 :(得分:3)

spark.sql.shuffle.partitions用作上限。分区的最终数量为1 <= partitions <= spark.sql.shuffle.partition


如前所述,Spark中的排序通过RangePartitioner进行。它试图实现的是将数据集划分为大致相等范围的指定数量(spark.sql.shuffle.partition)。

保证分区后相等的值将在同一分区中。值得检查RangePartitioning(不是公共API的一部分)类文档:

  

...

     

ordering中的表达式求值相同的所有行都将在同一分区中

并且如果不同的排序值的数量小于所需的分区数量,即,可能的范围数量小于spark.sql.shuffle.partition,则最终将得到较少的分区数量。另外,这是RangePartitioner Scaladoc的引用:

  

RangePartitioner创建的实际分区数可能   在以下情况下,该参数与partitions参数不同   采样记录的数量小于分区的值。

回到您的示例,n是一个常量("a"),无法分区。另一方面,i可以有10,000个可能的值,并被划分为200(=spark.sql.shuffle.partition)个范围或分区。

请注意,这仅适用于DataFrame / Dataset API。使用RDD的sortByKey时,可以明确指定分区数,或者Spark将使用当前分区数。

另请参阅:

答案 1 :(得分:1)

除了查看范围分区以进行排序外,我还进行了各种测试,以便从经验上更深入地进行研究-这是问题的症结所在。参见How does range partitioner work in Spark?

已尝试使用问题示例中的“ n”的1个不同值和“ n”的1个以上的不同值,然后对 df.orderBy($“使用各种数据帧大小n“)

  • 很显然,对于 确定分区的数量,该分区将包含随后要通过mapPartitions进行排序的数据范围
  • 基于对现有分区的采样,然后针对这些计算范围计算启发式最佳分区数,
  • 在大多数情况下将计算并因此生成N + 1个分区,从而 N + 1个分区为空

分配的额外分区几乎总是空的事实使我认为编码中存在某种计算错误,换句话说就是一个小虫子。

我基于以下简单测试,该测试确实返回了我认为应该认为是适当数量的分区的RR:

val df_a1 = (1 to 1).map(i => ("a",i)).toDF("n","i").cache
val df_a2 = (1 to 1).map(i => ("b",i)).toDF("n","i").cache
val df_a3 = (1 to 1).map(i => ("c",i)).toDF("n","i").cache
val df_b = df_a1.union(df_a2)
val df_c = df_b.union(df_a3)

df_c.orderBy($"n")
 .rdd
 .mapPartitionsWithIndex{case (i,rows) => Iterator((i,rows.size))}
 .toDF("partition_number","number_of_records")
 .show(100,false)

返回:

+----------------+-----------------+
|partition_number|number_of_records|
+----------------+-----------------+
|0               |1                |
|1               |1                |
|2               |1                |
+----------------+-----------------+

此边界示例计算非常简单。只要对任何“ n”使用 1到2 1 .. N ,就会产生多余的空分区:

+----------------+-----------------+
|partition_number|number_of_records|
+----------------+-----------------+
|0               |2                |
|1               |1                |
|2               |1                |
|3               |0                |
+----------------+-----------------+

排序要求给定“ n”或一组“ n”的所有数据都在同一分区中。