累加器
1.定义
-
累加器是分布式的共享只写变量
共享:累加器的值由Driver端共享给Executor端
只写:Executor端互相之间读取不到对方的累加器 -
累加器可以替换一些需要shuffle的操作
2.问题引入
package SparkCore._06_累加器
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
/**
* yatolovefantasy
* 2021-10-10-22:18
*/
object _01_ {
def main(args: Array[String]): Unit = {
val sc: SparkConf = new SparkConf().setMaster("local[*]").setAppName(" ")
val sparkContext = new SparkContext(sc)
val sourceRDD: RDD[Int] = sparkContext.makeRDD(List(1,2,3,4))
//TODO 1.用累加替换reduce()
//直接遍历就完事了
var sum = 0;
sourceRDD.foreach(
num=>{
sum += num
}
)
println("sum="+sum)
sparkContext.stop()
}
}
执行结果为0
3.原因分析
Driver端的变量,通过RDD的算子闭包发送到Executor端,Executor分别获取自己分区的数据,还有计算逻辑。
executor1计算: sum = 1+2
executor2计算: sum = 3+4
但是Executor计算完后,应该将计算结果返回到Driver端,Spark闭包只知道将数据带到Executor,不知道往Driver端返回。
引入累加器
累加器是Spark中第二种数据结构,Spark会将累加器从Driver传递到Executor,再将结果返回到Driver。
累加器用来把Executor端变量信息聚合到Driver端,在Driver程序中定义的变量,在Executor端的每个Task都会得到这个变量的一份新的副本,每个task更新这些副本的值后,传回Driver端进行merge
累加器的使用
package SparkCore._06_累加器
import org.apache.spark.{SparkConf, SparkContext}
/**
* yatolovefantasy
* 2021-10-10-22:36
*/
object _02_ {
def main(args: Array[String]): Unit = {
val conf : SparkConf = new SparkConf().setMaster("local[*]").setAppName(" ")
val sc = new SparkContext(conf)
val rdd = sc.makeRDD(List(1,2,3,4,5))
//todo 1.获取系统的累加器,Spark默认提供了简单的数据聚合的累加器
var sum = sc.longAccumulator("sum"); //sum是累加器的名字
rdd.foreach(
num => {
// todo 2.使用累加器
sum.add(num)
}
)
// todo 3.获取累加器的值
println("sum = " + sum.value)
}
}
系统累加器有:longAccumulator,DoubleAccumulator,还有CollectionAccumulator(List)
累加器使用注意事项
1.少加问题
val rdd = sc.makeRDD(List(1,2,3,4,5))
// 获取系统的累加器,Spark默认提供了简单的数据聚合的累加器
var sum = sc.longAccumulator("sum"); //sum是累加器的名字
rdd.map(
num => {
// 使用累加器
sum.add(num)
num
}
)
// 获取累加器的值
println("sum = " + sum.value)
执行结果:0
少加问题:如果在转换算子中调用累加器,如果没有行动算子的话,那么不会执行
解决:添加一个行动算子
package SparkCore._06_累加器
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
/**
* yatolovefantasy
* 2021-10-10-22:46
*/
object _03_ {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName(" ")
val sc = new SparkContext(conf)
val rdd = sc.makeRDD(List(1, 2, 3, 4, 5))
var sum = sc.longAccumulator("sum"); //sum是累加器的名字
val value: RDD[Int] = rdd.map(
num => {
// 使用累加器
sum.add(num)
num
}
)
value.collect()
// 获取累加器的值
println("sum = " + sum.value)
}
}
执行结果为:sum = 15
2.多加问题
val rdd = sc.makeRDD(List(1,2,3,4,5))
// 获取系统的累加器,Spark默认提供了简单的数据聚合的累加器
var sum = sc.longAccumulator("sum"); //sum是累加器的名字
val mapRDD = rdd.map(
num => {
// 使用累加器
sum.add(num)
num
}
)
mapRDD.collect()
mapRDD.collect()
// 获取累加器的值
println("sum = " + sum.value)
执行结果:30
多加问题:累加器在SparkContext环境中是全局共享的,行动算子调用一次会执行一次,如果有多个行动算子,就会累加多次。
3.累加器一般使用方式
解决方法:一般情况下,将累加器放在行动算子中进行操作。
自定义累加器
package SparkCore._06_累加器
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.util.AccumulatorV2
import scala.collection.mutable
/**
* yatolovefantasy
* 2021-10-10-23:00
*
* 自定义累加器 实现wordCount
*/
object _04_diy {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName(" ")
val sc = new SparkContext(conf)
val rdd = sc.makeRDD(List("hello","Spark","hello"))
//todo 1.创建累加器
val wcAcc = new myAcc()
//todo 2.注册
sc.register(wcAcc,"wordCountAcc")
//todo 3.使用
rdd.foreach(
word=>{
wcAcc.add(word)
}
)
//todo 4.获取
println(wcAcc.value)
}
}
//6个方法
//泛型:IN 累加器输入的数据 out 累加器输出的数据类型
class myAcc extends AccumulatorV2[String,mutable.Map[String,Long]] {
//创建一个Map做返回值
private var wcMap = mutable.Map[String,Long]()
//累加
override def add(word: String): Unit = {
val cnt: Long = wcMap.getOrElse(word, 0l)
wcMap.update(word,cnt+1)
}
override def value: mutable.Map[String, Long] = {
wcMap
}
//Driver端合并多个累加器
override def merge(other: AccumulatorV2[String, mutable.Map[String, Long]]): Unit = {
val map1 = this.wcMap
val map2 = other.value
map2.foreach{
case (word,cnt) =>{
val newCnt = map1.getOrElse(word,0l) + cnt
map1.update(word,newCnt)
}
}
}
override def isZero: Boolean = {
wcMap.isEmpty
}
override def copy(): AccumulatorV2[String, mutable.Map[String, Long]] = {
new myAcc()
}
override def reset(): Unit = wcMap.clear()
}
Map(Spark -> 1, hello -> 2)