在pyspark中,为什么`limit`后跟`repartition`会创建完全相等的分区大小?

时间:2019-02-22 21:01:01

标签: python apache-spark pyspark

根据pyspark documentationrepartition应该使用哈希分区,这将导致分区大小略有不等。但是,我发现通过在其前面加上limit,它将产生完全相等的分区大小。这可以通过在pyspark shell中运行以下命令来显示:

df = spark.createDataFrame([range(5)] * 100)

def count_part_size(part_iter):
    yield len(list(part_iter))

print(df.repartition(20).rdd.mapPartitions(count_part_size).collect())
# [4, 4, 4, 5, 4, 4, 5, 4, 5, 6, 6, 6, 7, 5, 5, 5, 5, 6, 5, 5]

print(df.limit(100).repartition(20).rdd.mapPartitions(count_part_size).collect())
# [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]

如果repartition使用哈希分区程序,为什么在这种情况下会产生完全相等的分区大小?如果未使用哈希分区程序,则使用哪种分区程序?

顺便说一句,我正在使用python版本2.7.15和spark版本2.0.2

1 个答案:

答案 0 :(得分:4)

这里有四个因素:

  • 如果未提供分区表达式,则repartition不使用HashPartitioning,或者具体地说,它不直接使用它。相反,它使用RoundRobinPartitioningwhich(您可能会猜到)

      

    从随机分区开始,在输出分区之间均匀分配元素。

    内部,它在每个分区starting from a random point上生成scala.Int序列。仅这些值通过HashPartitioner传递。

  • 之所以这样工作是因为Int hashCode仅仅是身份-换句话说

    ∀x∈Intx = hashCode(x)

    (这与Scala hash范围--2147483648到2147483647中的CPython Int具有相同的行为。这些散列根本不是设计为加密安全的)因此应用了{{1 }}到一系列HashPartitioner的值会导致实际的Round Robin分配。

    因此,在这种情况下,Int仅作为模运算符。

  • 您在重新分区之前应用了HashPartitioner,因此所有值都首先被改组到单个节点。因此,仅使用LIMIT值的一个序列。

  • 分区数是数据集大小的除数。因此,数据可以在分区之间均匀分布。

总体而言,它是预期行为(每个分区应在输出分区之间均匀分布),管道的属性(只有一个输入分区)和数据(数据集可以均匀分布)的组合。