1. RDD1-累加器
- 本身分布式的程序中不能直接累加的,需要借助于共享变量
- 分布式的程序中,在Driver端定义的数值,该数值在Executor端执行真正的计算,当Executor执行完毕后,该数值不会返回得到Driver端,所以Driver拿不到该结果
- 所以引出了在Driver端和Executor端共享的变量,在Driver端定义的数值在Executor计算完毕后,是可以直接返回结果
引出: Spark提供的Accumulator,主要用于多个节点对一个变量进行共享性的操作
- sc.accumulator(0)
- sc.longaccumulater(0) //用这个
- acccount.add()
- 使用累加器: 通常在向 Spark 传递函数时,比如使用 map() 函数或者用 filter() 传条件时,可以使用驱动器driver程序中定义的变量,但是集群中运行的每个任务都会得到这些变量的一份新的副本,更新这些副本的值也不会影响驱动器Driver中的对应变量。这时使用累加器就可以实现我们想要的效果。
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 or Executor)缓存只读变量,而不是在Task之间传递这些变量。使用广播变量能够高效地在集群每个节点创建大数据集的副本。同时Spark还使用高效的广播算法分发这些变量,从而减少通信的开销。
案例:
代码演示: 定义一个id+水果的Array的数据结构,这里实现通过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()
}
}