累加器和广播变量
1. 共享变量
累加器 (accumulator):用来对信息进行聚合,主要用于累计计数等场景;
广播变量 (broadcast variable):主要用于在节点间高效分发大对象。
2. 理解闭包
2.1 Scala 中闭包的概念
这里先介绍一下 Scala 中关于闭包的概念:
var more = 10
val addMore = (x: Int) => x + more
x : 是一个绑定变量 (bound variable),因为其是该函数的入参,在函数的上下文中有明确的定义;
more : 是一个自由变量 (free variable),因为函数字面量本生并没有给 more 赋予任何含义。
按照定义:在创建函数时,如果需要捕获自由变量,那么包含指向被捕获变量的引用的函数就被称为闭包函数。
2.2 Spark 中的闭包
在实际计算时,Spark 会将对 RDD 操作分解为 Task,Task 运行在 Worker Node 上。
在执行之前,Spark 会对任务进行闭包,如果闭包内涉及到自由变量,则程序会进行拷贝,并将副本变量放在闭包中,之后闭包被序列化并发送给每个执行者。
因此,当在 foreach 函数中引用 counter 时,它将不再是 Driver 节点上的 counter,而是闭包中的副本 counter,默认情况下,副本 counter 更新后的值不会回传到 Driver,所以 counter 的最终值仍然为零。
累加器的原理实际上很简单:就是将每个副本变量的最终值传回 Driver,由 Driver 聚合后得到最终值,并更新原始变量。
累加器在Driver端定义,在Executor端计算,计算结束之后,把各个分区的计算结果收集一下到Driver端,再做累加

import org.apache.spark.rdd.RDD import org.apache.spark.util.{AccumulatorV2, LongAccumulator} import org.apache.spark.{SparkConf, SparkContext} //累加器 object accumulator { def main(args: Array[String]): Unit = { //本地模式 val conf: SparkConf = new SparkConf().setAppName("My scala word count").setMaster("local") //创建spark上下文对象 val sc = new SparkContext(conf) val dataRDD: RDD[Int] = sc.makeRDD(List(1,2,3,4,5)) var sum=0 val unit: RDD[Unit] = dataRDD.map(x => { sum += x }) unit.collect() println(sum) //累加器 val sum2: LongAccumulator = sc.longAccumulator val unit2: RDD[Unit] = dataRDD.map(x => { sum2.add(x) }) unit2.collect() println(sum2.value) var data: RDD[String] = sc.makeRDD(List("hadoop","hive","python","linux"),2) // 创建累加器 val wordAccumulator = new WordAccumulator //注册累加器 sc.register(wordAccumulator) data.foreach{ case word =>{ wordAccumulator.add(word) } } //获取累加器的值 println(wordAccumulator.value) } } //声明累加器 //1. 继承AccumulatorV2 //2. 实现抽象方法 //3. 创建累加器 class WordAccumulator extends AccumulatorV2 [String,java.util.ArrayList[String]]{ val list = new java.util.ArrayList[String]() //当前的累加器是否为初始状态 override def isZero: Boolean = { list.isEmpty() } //复制累加器对象 override def copy():AccumulatorV2[String,java.util.ArrayList[String]]={ new WordAccumulator() } //重置累加器对象 override def reset(): Unit = { list.clear() } //向累加器中添加元素 override def add(v: String): Unit = { if(v.contains("h")){ list.add(v) } } //合并累加器 override def merge(other: AccumulatorV2[String, java.util.ArrayList[String]]): Unit = { list.addAll(other.value) } //获取累加器的结果 override def value: java.util.ArrayList[String]=list }
3、广播变量
在上面介绍中闭包的过程中我们说道每个 Task 任务的闭包都会持有自由变量的副本,如果变量很大且 Task 任务很多的情况下,这必然会对网络 IO 造成压力,为了解决这个情况,Spark 提供了广播变量。
广播变量的做法很简单:就是不把副本变量分发到每个 Task 中,而是将其分发到每个 Executor,Executor 中的所有 Task 共享一个副本变量。
import org.apache.spark.broadcast.Broadcast import org.apache.spark.{SparkConf, SparkContext} //累加器 object broadcast { def main(args: Array[String]): Unit = { //本地模式 val conf: SparkConf = new SparkConf().setAppName("My scala word count").setMaster("local") //创建spark上下文对象 val sc = new SparkContext(conf) val broadcastVar: Broadcast[List[Int]] = sc.broadcast(List(1,2,3,4,5)) val result: List[Int] = broadcastVar.value.map(x=>x*10) println(result) } }
posted on 2020-10-09 11:20 happygril3 阅读(306) 评论(0) 收藏 举报
浙公网安备 33010602011771号