web-dev-qa-db-ja.com

Spark集計関数-aggregateByKeyはどのように機能しますか?

3つのノードに分散システムがあり、それらのノード間でデータが分散されているとします。たとえば、3つのノードすべてに存在するtest.csvファイルがあり、次の2列が含まれています。

_**row   | id,  c.**
---------------
row1  | k1 , c1  
row2  | k1 , c2  
row3  | k1 , c3  
row4  | k2 , c4  
row5  | k2 , c5  
row6  | k2 , c6  
row7  | k3 , c7  
row8  | k3 , c8  
row9  | k3 , c9  
row10 | k4 , c10   
row11 | k4 , c11  
row12 | k4 , c12 
_

次に、SparkContext.textFileを使用して、ファイルをrddなどとして読み取ります。私の知る限り、各spark=ワーカーノードはファイルから一部を読み取ります。したがって、今は各ノードが以下を格納するとしましょう。

  • ノード1:行1〜4
  • ノード2:行5〜8
  • ノード3:行9〜12

私の質問は、これらのデータに対して計算を行いたいと考えていることです。キーをグループ化する必要があるため、キーと値のペアは_[k1 [{k1 c1} {k1 c2} {k1 c3}]].._などになります。

groupByKey()と呼ばれる関数は非常に高価であり、aggregateByKey()を使用することをお勧めします。だから私はgroupByKey()aggregateByKey()が内部でどのように機能するのか疑問に思っていますか?上記の例を使用して説明してもらえますか?各ノードのどこに行があるかをシャッフルしましたか?

40
EdwinGuo

aggregateByKey()は、reduceByKey()の開始値を指定することを除き、combineByKey()とほぼ同じです(両方とも舞台裏でaggregateByKey()を呼び出します)。ほとんどの人はreduceByKey()に精通しているので、それを説明で使用します。

reduceByKey()が非常に優れている理由は、コンバイナと呼ばれるMapReduce機能を使用するためです。 _+_や_*_などの関数は、呼び出される要素の順序は関係ないため、この方法で使用できます。これにより、Spark=がすべて同じパーティションにない場合でも、同じキーで値を「削減」し始めることができます。

反対に、groupByKey()は、Iterableを受け取る関数を記述するため、より汎用性が高くなります。つまり、すべての要素を配列に入れることもできます。ただし、_(K,V,)_ペアの完全なセットが1つのパーティションに存在する必要があるため、非効率的です。

タイプを減らす操作でデータを移動するステップは、一般的にshuffleと呼ばれます。最も単純なレベルでは、データは各ノードに分割され(多くの場合、ハッシュパーティショナーを使用)、それぞれにソートされますノード。

50
aaronman

aggregateByKey()はreduceByKeyとはまったく異なります。何が起こるかは、reduceByKeyがaggregateByKeyの特定のケースの一種であるということです。

aggregateByKey()は特定のキーの値を結合します。そのような結合の結果は、指定した任意のオブジェクトになります。 1つのパーティション(同じノードで実行される)内で値を結合(「追加」)する方法と、異なるパーティション(異なるノードにある可能性がある)の結果を結合する方法を指定する必要があります。 reduceByKeyは、組み合わせの結果(合計など)が値と同じタイプであり、異なるパーティションから結合されたときの操作も、値を結合するときの操作と同じという意味で、特定のケースです。パーティション。

例:ペアのリストがあると想像してください。それを並列化します:

val pairs = sc.parallelize(Array(("a", 3), ("a", 1), ("b", 7), ("a", 5)))

今度は、キーを合計してそれらを「結合」したいと思います。この場合、reduceByKeyとaggregateByKeyは同じです。

val resReduce = pairs.reduceByKey(_ + _) //the same operation for everything
resReduce.collect
res3: Array[(String, Int)] = Array((b,7), (a,9))

//0 is initial value, _+_ inside partition, _+_ between partitions
val resAgg = pairs.aggregateByKey(0)(_+_,_+_)
resAgg.collect
res4: Array[(String, Int)] = Array((b,7), (a,9))

ここで、集約を値のセット、つまり値とは異なるタイプ、つまり整数(整数の合計も整数)にしたいことを想像してください。

import scala.collection.mutable.HashSet
//the initial value is a void Set. Adding an element to a set is the first
//_+_ Join two sets is the  _++_
val sets = pairs.aggregateByKey(new HashSet[Int])(_+_, _++_)
sets.collect
res5: Array[(String, scala.collection.mutable.HashSet[Int])]  =Array((b,Set(7)), (a,Set(1, 5, 3)))
64
Antoni