reduceByKey:它在内部如何工作?

时间:2015-05-09 21:43:52

标签: scala apache-spark rdd

我是Spark和Scala的新手。我对reduceByKey函数在Spark中的工作方式感到困惑。假设我们有以下代码:

val lines = sc.textFile("data.txt")
val pairs = lines.map(s => (s, 1))
val counts = pairs.reduceByKey((a, b) => a + b)

地图功能是明确的:s是键,它指向data.txt的行,1是值。

但是,我还没有得到reduceByKey如何在内部工作?是" a"指向钥匙?或者," a"指向" s"?那么什么代表a + b?它们是如何填满的?

4 个答案:

答案 0 :(得分:78)

让我们将其分解为离散的方法和类型。这通常暴露了新开发者的复杂性:

pairs.reduceByKey((a: Int, b: Int) => a + b)

成为

pairs.reduceByKey((accumulatedValue: Int, currentValue: Int) => accumulatedValue + currentValue)

并重命名变量使其更加明确

pairs.reduce((accumulatedValue: List[(String, Int)], currentValue: (String, Int)) => {
  //Turn the accumulated value into a true key->value mapping
  val accumAsMap = accumulatedValue.toMap   
  //Try to get the key's current value if we've already encountered it
  accumAsMap.get(currentValue._1) match { 
    //If we have encountered it, then add the new value to the existing value and overwrite the old
    case Some(value : Int) => (accumAsMap + (currentValue._1 -> (value + currentValue._2))).toList
    //If we have NOT encountered it, then simply add it to the list
    case None => currentValue :: accumulatedValue 
  }
})

因此,我们现在可以看到,我们只是为给定的密钥获取累计值,并将其与该密钥的下一个值相加。现在,让我们进一步分解,以便我们理解关键部分。所以,让我们更像这样的方法:

res/values/styles.xml

因此,您可以看到reduce ByKey 采用了找到密钥并跟踪它的样板,以便您不必担心管理该部分。

更深入,如果你想要更真实

所有这一切,这是所发生的事情的简化版本,因为这里有一些优化。此操作是关联的,因此火花引擎将首先在本地执行这些减少(通常称为地图侧减少),然后再次在驾驶员处执行。这节省了网络流量;而不是发送所有数据并执行操作,它可以尽可能小地减少它,然后通过线路发送减少量。

答案 1 :(得分:39)

reduceByKey函数的一个要求是必须是关联的。为了对reduceByKey如何工作建立一些直觉,让我们首先看看关联关联函数如何帮助我们进行并行计算:

associative function in action

正如我们所看到的,我们可以分解原始集合,并通过应用关联函数,我们可以累积总数。连续的情况是微不足道的,我们习惯了:1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10.

关联性允许我们按顺序和并行使用相同的功能。 reduceByKey使用该属性计算RDD的结果,RDD是由分区组成的分布式集合。

考虑以下示例:

// collection of the form ("key",1),("key,2),...,("key",20) split among 4 partitions
val rdd =sparkContext.parallelize(( (1 to 20).map(x=>("key",x))), 4)
rdd.reduceByKey(_ + _)
rdd.collect()
> Array[(String, Int)] = Array((key,210))

在spark中,数据被分配到分区中。对于下一个图示,(4)分区在左侧,用细线包围。首先,我们在分区中按顺序将函数本地应用于每个分区,但我们并行运行所有4个分区。然后,通过再次应用相同的函数来聚合每个局部计算的结果,最后得到一个结果。

enter image description here

reduceByKeyaggregateByKey aggregateByKey的特化,有2个函数:一个应用于每个分区(顺序),另一个应用于每个分区的结果(并行) )。 reduceByKey在两种情况下都使用相同的关联函数:对每个分区进行顺序计算,然后将这些结果组合成最终结果,如我们在此处所示。

答案 2 :(得分:6)

在你的

示例中
val counts = pairs.reduceByKey((a,b) => a+b)

ab都是Int_2元组的pairs累加器。 reduceKey将使用具有相同值s的两个元组,并将其_2值用作ab,从而生成新的Tuple[String,Int]。重复此操作,直到每个键s只有一个元组。

与非 Spark (或者,实际上,非并行)reduceByKey不同,其中第一个元素始终是累加器而第二个元素是值,reduceByKey在一个值中运行分布式方式,即每个节点将其元组集合减少为唯一键控元组的集合,然后从多个节点中减少元组,直到最终的唯一键控一组元组。这意味着当节点的结果减少时,ab表示已经减少了累加器。

答案 3 :(得分:0)

Spark RDD reduceByKey函数使用关联的reduce函数合并每个键的值。

reduceByKey函数仅在RDD上起作用,这是一个转换操作,意味着它被延迟计算。然后,将关联函数作为参数传递,并将其应用于源RDD并创建一个新的RDD。

因此,在您的示例中,rdd对具有一组多个成对的元素,例如(s1,1),(s2,1)等。并且reduceByKey接受函数(accumulator,n)=>(accumulator + n),该函数将累加器变量初始化为默认值0,然后将每个键的元素相加,然后返回结果总数与键配对的rdd计数。