累加器和广播变量
累加器
在创建函数时,如果需要捕获自由变量,那么包含指向被捕获变量的引用的函数就被称为闭包函数。在实际计算时,Spark 会将对 RDD 操作分解为 Task,Task 运行在 Worker Node 上。在执行之前,Spark 会对任务进行闭包,如果闭包内涉及到自由变量,则程序会进行拷贝,并将副本变量放在闭包中,之后闭包被序列化并发送给每个执行者。因此,当在 foreach 函数中引用 counter 时,它将不再是 Driver 节点上的 counter,而是闭包中的副本 counter,默认情况下,副本 counter 更新后的值不会回传到 Driver,所以 counter 的最终值仍然为零。
内置累加器
自定义累加器new一个之后,方法add之中就行了。
自定义累加器
自定义的累加器需要继承AccumulatorV2并实现以下方法
- copy() Creates a new copy of this accumulator.
- reset() Resets this accumulator, which is zero value
- merge() 分区间的累加器的合并
- add()一个分区内的数据的累增
- value() 返回当前缓存的值
class MapAcc extends AccumulatorV2[Long,Map[String,Double]]{
private var map: Map[String, Double] = Map[String, Double]()
override def isZero: Boolean = map.isEmpty
override def copy(): AccumulatorV2[Long, Map[String, Double]] = {
val acc = new MapAcc
acc.map=map
acc
}
override def reset(): Unit = map = Map[String, Double]()
override def add(v: Long): Unit = {
// sum求和,count计数
map += "sum" -> (map.getOrElse("sum",0D)+v)
map += "count" -> (map.getOrElse("count",0D)+1)
}
override def merge(other: AccumulatorV2[Long, Map[String, Double]]): Unit = other match {
case o:MapAcc =>
this.map += "sum" -> (this.map.getOrElse("sum",0D)+o.map.getOrElse("sum",0D))
this.map += "count" -> (this.map.getOrElse("count",0D)+o.map.getOrElse("count",0D))
case _ => throw new UnsupportedOperationException
}
override def value: Map[String, Double] = {
this.map += "avg" ->(this.map.getOrElse("sum",0D)/this.map.getOrElse("count",1D))
map
}
}
调用时new一个分区器,然后使用add方法进行递增
注意:累加器最好用在行动算子之中
广播变量
如果说累加器是共写变量,那么广播变量就是共读变量。广播变量通过调用SparkContext.broadcast(v)来创建.实际是对v的一个包装,广播后在所有的节点上都可以通过.value获得该值。