spark的distinct()函数是否仅对每个分区中的不同元组进行洗牌

时间:2015-12-18 20:49:07

标签: python apache-spark pyspark

据我所知,distinct()散列分区RDD以识别唯一键。但它是否优化了每个分区只移动不同的元组?

想象一下带有以下分区的RDD

  1. [1,2,2,1,4,2,2]
  2. [1,3,3,5,4,5,5,5]
  3. 在这个RDD的一个不同的地方,所有重复的密钥(分区1中的2s和分区2中的5s)是否会被混洗到它们的目标分区,或者只有每个分区的不同密钥被混洗到目标?

    如果所有键都被洗牌,那么带有set()操作的aggregate()将减少shuffle。

    def set_update(u, v):
        u.add(v)
        return u
    rdd.aggregate(set(), set_update, lambda u1,u2: u1|u2)
    

1 个答案:

答案 0 :(得分:5)

unique通过reduceByKey(element, None)实施。因此,它每个分区只会刷新唯一值。如果重复数量很少,那么它仍然是非常昂贵的操作。

有些情况下使用set会很有用。特别是如果您在distinct上致电PairwseRDD,您可能更愿意aggregateByKey / combineByKey来同时按键实现重复数据删除和分区。特别要考虑以下代码:

rdd1 = sc.parallelize([("foo", 1), ("foo", 1), ("bar", 1)])
rdd2 = sc.parallelize([("foo", "x"), ("bar", "y")])
rdd1.distinct().join(rdd2)

必须将rdd1拖放两次 - 一次用于distinct,一次用于join。相反,您可以使用combineByKey

def flatten(kvs):
    (key, (left, right)) = kvs
    for v in left:
        yield (key, (v, right))

aggregated = (rdd1
    .aggregateByKey(set(), set_update, lambda u1, u2: u1 | u2))

rdd2_partitioned = rdd2.partitionBy(aggregated.getNumPartitions())

(aggregated.join(rdd2_partitioned)
    .flatMap(flatten))

注意

join逻辑在Scala中有点不同于Python(PySpark使用union后跟groupByKey,请参阅Spark RDD groupByKey + join vs join performance获取Python和Scala DAG),因此我们必须在调用join之前手动对第二个RDD进行分区。