簡體   English   中英

reduceByKey:它在內部是如何工作的?

[英]reduceByKey: How does it work internally?

我是 Spark 和 Scala 的新手。 我對 reduceByKey function 在 Spark 中的工作方式感到困惑。 假設我們有以下代碼:

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

map function 很明確:s 是鍵,它指向data.txt中的行,1 是值。

但是,我不明白 reduceByKey 在內部是如何工作的? “a”是否指向鍵? 或者,“a”是否指向“s”? 那么什么代表a + b? 它們是如何填充的?

讓我們分解為離散的方法和類型。 這通常暴露了新開發者的錯綜復雜:

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

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 
  }
})

因此,您可以看到reduce ByKey采用尋找密鑰並跟蹤它的樣板,以便您不必擔心管理該部分。

更深入,更真實,如果你想

所有這一切,這是一個簡化的版本,因為這里有一些優化。 此操作是關聯的,因此火花引擎將首先在本地執行這些減少(通常稱為地圖側減少),然后再次在駕駛員處執行。 這節省了網絡流量; 而不是發送所有數據並執行操作,它可以盡可能小地減少它,然后通過線路發送減少量。

reduceByKey函數的一個要求是必須是關聯的。 為了建立一些關於reduceByKey如何工作的直覺,讓我們首先看看關聯關聯函數如何幫助我們進行並行計算:

聯合功能在行動中

正如我們所看到的,我們可以分解原始集合,並通過應用關聯函數,我們可以累積總數。 連續的情況是微不足道的,我們習慣了它: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個分區。 然后,通過再次應用相同的函數來聚合每個局部計算的結果,最后得到結果。

在此輸入圖像描述

reduceByKey是的專業化aggregateByKey aggregateByKey需要2層的功能:被施加到被每個分區(並行)的結果中應用的每個分區(順序地)和一個一個。 reduceByKey在兩種情況下使用相同的關聯函數:在每個分區上執行順序計算,然后將這些結果組合在最終結果中,如此處所示。

在你的例子中

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

ab都是pairs的元組_2Int累加器。 reduceKey將使用具有相同值s兩個元組並將其_2值用作ab ,從而生成新的Tuple[String,Int] 重復此操作,直到只有一個為每個鍵元組s

與非Spark (或實際上,非並行) reduceByKey ,其中第一個元素始終是累加器,第二個元素是值, reduceByKey以分布式方式運行,即每個節點將其元組集合減少為唯一的集合 - 鍵控元組,然后從多個節點減少元組,直到有一組最終唯一鍵控元組。 這意味着當節點的結果減少時, ab表示已減少的累加器。

Spark RDD reduceByKey函數使用關聯reduce函數合並每個鍵的值。

reduceByKey函數僅適用於RDD,這是一個轉換操作,意味着它被懶惰地評估。 並且關聯函數作為參數傳遞,該參數應用於源RDD並作為結果創建新的RDD。

所以在你的例子中,rdd對有一組多個配對元素,如(s1,1),(s2,1)等。並且reduceByKey接受一個函數(accumulator,n)=>(累加器+ n),它初始化累加器變量為默認值0並為每個鍵添加元素並返回結果rdd計數具有與鍵配對的總計數。

如果您的輸入 RDD 數據如下所示,則很簡單: (aa,1) (bb,1) (aa,1) (cc,1) (bb,1)

如果你在上面的rdd數據上應用reduceByKey,那么你需要記住的很少,reduceByKey總是需要2個輸入(x,y)並且總是一次處理兩行。 由於它是reduceByKey,它將組合兩行相同的鍵並組合值的結果。

val rdd2 = rdd.reduceByKey((x,y) => x+y) rdd2.foreach(println)

output: (aa,2) (bb,2) (cc,1)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM