1.RDD1-アキュムレータ
- 分散プログラム自体に直接蓄積することはできません。共有変数の助けが必要です。
- 分散プログラムでは、ドライバー側で定義された値がエグゼキューター側で実際の計算を実行します。エグゼキューターが実行されると、値がドライバー側に返されないため、ドライバーは結果を取得できません。
- したがって、ドライバーとエグゼキューターの間で共有される変数につながります。ドライバーで定義された値は、エグゼキューターが計算された直後に結果を返すことができます。
リード: Sparkが提供するアキュムレータは、主に複数のノードによる変数の共有操作に使用されます
- sc.accumulator(0)
- sc.longaccumulater(0)//これを使用
- acccount.add()
- アキュムレータを使用する:通常、map()関数やfilter()を使用して条件を渡すなど、関数をSparkに渡す場合、ドライバプログラムで定義された変数を使用できますが、クラスタで実行されている各タスクはこれらの変数を取得します新しいコピーの場合、これらのコピーの値を更新しても、ドライバーの対応する変数には影響しません。このとき、アキュムレータを使用して、必要な効果を得ることができます。
val xx:Accumulator [Int] = sc.accumulator(0)
コードデモ:Scalaコレクション
import org.apache.spark.rdd.RDD
import org.apache.spark.util.LongAccumulator
import org.apache.spark.{
Accumulator, SparkConf, SparkContext}
/**
* @author liu a fu
* @date 2021/1/17 0017
* @version 1.0
* @DESC 演示Spark的共享变量之累加器
*/
object SparkAddCumulater {
val conf: SparkConf = new SparkConf().setAppName(this.getClass.getSimpleName.stripSuffix("$")).setMaster("local[*]")
def main(args: Array[String]): Unit = {
val sc = new SparkContext(conf)
//实验1 Scala中的累加器
val arr = Seq(1, 2, 3)
var count1 = 0
arr.foreach(x => count1 += x)
println(count1) //6
//实验2 RDD的累加器
//因为定义变量在driver端定义,但是rdd的数值的计算在exector中执行的,执行完毕后没有直接返回driver端,看不到结果
var count2=0
val rdd1: RDD[Int] = sc.parallelize(arr)
rdd1.foreach(x => count2 +=x)
println(count2) //0
//实验3:构建在Driver端和Executor的共享的变量
val count3: Accumulator[Int] = sc.accumulator(0) //底层实现的+=,仅仅能够在driver访问 过时
rdd1.foreach(x => count3.add(x))
println(count3) //6
//实验4:使用建议使用的方法
//Create and register a long accumulator, which starts with 0 and accumulates inputs by `add`. 源码解释
val count4: LongAccumulator = sc.longAccumulator("count4")
rdd1.foreach(x => count4.add(x))
println(count4) //LongAccumulator(id: 51, name: Some(count4), value: 6)
println(count4.value) //6
}
}
2.RDD2-ブロードキャスト変数
原則:
ブロードキャスト変数を使用すると、開発者は、タスク間でこれらの変数を渡す代わりに、各ノード(WorkerまたはExecutor)に読み取り専用変数をキャッシュできます。ブロードキャスト変数を使用すると、クラスターの各ノードに大きなデータセットのコピーを効率的に作成できます。同時に、Sparkは効率的なブロードキャストアルゴリズムを使用してこれらの変数を分散し、通信のオーバーヘッドを削減します。
ケース:
コードのデモンストレーション:id + fruit配列のデータ構造を定義します。ここではidごとにフルーツを見つけることができます。
import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD
import org.apache.spark.{
SparkConf, SparkContext}
/**
* @author liu a fu
* @date 2021/1/17 0017
* @version 1.0
* @DESC 演示广播变量
*/
object SparkBroadcast {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setAppName(this.getClass.getSimpleName.stripSuffix("$")).setMaster("local[*]")
val sc = new SparkContext(conf)
val kvFruit: RDD[(Int, String)] = sc.parallelize(List((1, "apple"), (2, "orange"), (3, "banana"), (4, "grape")))
val fruitMap: collection.Map[Int, String] = kvFruit.collectAsMap()
//这里面在数据量小的时候可以使用下面的方式
val valueRDD: RDD[Int] = sc.parallelize(Seq(2, 1,3, 4))
valueRDD.map(x => fruitMap(x)).collect().foreach(println(_))
println("="*100)
//但是如果fruitMap水果种类特别多的,每一个rdd的Task都需要拉取副本,造成大量数据网络传输,建议使用广播变量
//这里面需要使用Map的数据结构,因为Array(1,"Apple),需要将array转化为Map结构
val broad: Broadcast[collection.Map[Int, String]] = sc.broadcast(kvFruit.collectAsMap())
valueRDD.map(x => broad.value(x)).collect().foreach(println(_))
sc.stop()
}
}