我正在学习spark,当我使用下面的表达式在pyspark shell中测试repartition()函数时,我发现了一个非常奇怪的结果:所有元素在repartition()
函数之后都属于同一个分区。
在这里,我使用glom()
来了解rdd中的分区。我期待repartition()
洗牌元素并在分区之间随机分配。只有当我使用新的分区数< =原始分区重新分区时才会发生这种情况。
在我的测试中,如果我设置了新的分区数>原来的分区,也没有观察到洗牌。我在这里做错了吗?
In [1]: sc.parallelize(range(20), 8).glom().collect()
Out[1]:
[[0, 1],
[2, 3],
[4, 5],
[6, 7, 8, 9],
[10, 11],
[12, 13],
[14, 15],
[16, 17, 18, 19]]
In [2]: sc.parallelize(range(20), 8).repartition(8).glom().collect()
Out[2]:
[[],
[],
[],
[],
[],
[],
[2, 3, 6, 7, 8, 9, 14, 15, 16, 17, 18, 19, 0, 1, 12, 13, 4, 5, 10, 11],
[]]
In [3]: sc.parallelize(range(20), 8).repartition(10).glom().collect()
Out[3]:
[[],
[0, 1],
[14, 15],
[10, 11],
[],
[6, 7, 8, 9],
[2, 3],
[16, 17, 18, 19],
[12, 13],
[4, 5]]
我正在使用spark版本2.1.1。
答案 0 :(得分:2)
恭喜!您刚重新发现SPARK-21782 - 当numPartitions为2的幂时,重新分区会产生偏差:
PySpark使情况变得更糟,因为它uses batched serializer的默认批量大小等于10,所以每个分区上的项目数量都很少,所有这些都被拖拽到相同的输出。目前,重新分区算法(启用随机播放的合并)如下:
对于每个初始分区索引,生成位置为(new Random(index))。nextInt(numPartitions) 然后,对于初始分区索引中的元素号k,将其放入新分区位置+ k(模数numPartitions)。
因此,基本上元素在numPartitions桶上大致相同 - 从数字位置+ 1开始。
请注意,为每个初始分区索引创建一个新的Random实例,并使用固定的种子索引,然后将其丢弃。因此,对于世界上任何RDD的每个指数,该位置都是确定性的。此外,nextInt(绑定)实现有一个特殊情况,当bound是2的幂时,它基本上从初始种子中获取几个最高位,只有最小的加扰。
答案 1 :(得分:0)
啊,我认为与底层分区程序有关。我尝试了更大的数字,现在结果更有意义。
In [95]: [len(lst) for lst in sc.parallelize(range(1000), 8).glom().collect()]
Out[95]: [125, 125, 125, 125, 125, 125, 125, 125]
In [96]: [len(lst) for lst in sc.parallelize(range(1000), 8).repartition(10).glom().collect()]
Out[96]: [95, 95, 100, 105, 95, 95, 100, 105, 105, 105]
In [97]: [len(lst) for lst in sc.parallelize(range(1000), 8).repartition(5).glom().collect()]
Out[97]: [190, 195, 205, 210, 200]