根据pyspark documentation,repartition
应该使用哈希分区,这将导致分区大小略有不等。但是,我发现通过在其前面加上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
答案 0 :(得分:4)
这里有四个因素:
如果未提供分区表达式,则repartition
不使用HashPartitioning
,或者具体地说,它不直接使用它。相反,它使用RoundRobinPartitioning
,which(您可能会猜到)
从随机分区开始,在输出分区之间均匀分配元素。
内部,它在每个分区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
值的一个序列。
分区数是数据集大小的除数。因此,数据可以在分区之间均匀分布。
总体而言,它是预期行为(每个分区应在输出分区之间均匀分布),管道的属性(只有一个输入分区)和数据(数据集可以均匀分布)的组合。