累加器学完了,做个小总结,中间还是有些地方有点难度。
为什么要用累加器?
因为在Driver端定义的变量,如求和sum,在Executor端做求和计算之后,Eecutor端的sum是无法传回Driver端merge的。
val listRDD: RDD[(String, Long)] = sc.makeRDD(List(("xx",1L),("xx",2L),("yy",3L),2)
listRDD.foreach{
case (k,v)=>{
sum+=v
}
}
本来sum应该为6,但是实际是0
这时就轮到累加器上场了
Accumulator简介
Accumulator是spark提供的累加器,顾名思义,该变量只能够增加。
只有driver能获取到Accumulator的值,而Executor的Task只能对其做增加操作。你也可以为Accumulator命名(不支持Python),这样就会在spark web ui中显示,可以帮助你了解程序运行的情况。
累加器的作用:
累加器用来把Executor端变量信息聚合到Driver端。在Driver程序中定义的变量,在Executor端的每个Task都会得到这个变量的一份新的副本,每个task更新这些副本的值后,传回Driver端进行merge。
再看
var sum=0L
// 引入累加器
val accumulator: LongAccumulator = sc.longAccumulator
listRDD.foreach{
case (k,v)=>{
sum+=v
accumulator.add(v)
}
}
println(sum)
println(accumulator.value)
累加器完美解决
ok,搞清楚累加器是什么了,接下来就是自定义累加器(毕竟实际生产环境中定义好的累加器是远远不够用的)
//自定义累加器 ,
// 1. 继承AccumulatorV2,并设定泛型
// 2. 重写累加器的抽象方法
输入 (k,v) 输出sum值
class MyAccumulator extends AccumulatorV2[(String,Long),java.lang.Long]{
**//在累加器中维护一个Long类型的变量**
private var sum=0L
//累加器是否初始化
override def isZero: Boolean = {
sum==0
}
//复制新的累加器
override def copy(): AccumulatorV2[(String,Long), lang.Long] = {
new MyAccumulator
}
//重置累加器
override def reset(): Unit = {
sum=0L
}
**//最重要,将输入的值累加起来**
override def add(t:(String,Long)): Unit = {
sum+=t._2
}
//回到Driver端聚合
override def merge(other: AccumulatorV2[(String,Long), lang.Long]): Unit = {
sum+=other.value
}
//调用value方法即可得到最终的sum值
override def value: lang.Long = {
sum
}
}
接下来就是调用自定义的累加器
//创建累加器
val myaccu = new MyAccumulator
//注册累加器,让spark知道我的存在
sc.register(myaccu)
listRDD.foreach{
data=>{
myaccu.add(data)
}
}
println(myaccu.value)
可以看到,自定义累加器和直接调用spark的longAccumulator效果是一样的。
目前就写这些了。累加器的知识肯定不止这点,以后学的更多了再续上。
今天项目中用到了累加器。输出是HashMap。有些小坑,记录一下
val listRDD: RDD[(String, Long)] = sc.makeRDD(List(("xx",1L),("xx",2L),("yy",3L),("yy",3L),("yy",3L),("wy",3L)),2)
//创建累加器
val myaccu = new myAcc
//注册累加器,让spark直到我的存在
sc.register(myaccu)
listRDD.foreach{
data=>{
myaccu.add(data._1)
}
}
for ((k,v)<-myaccu.value){
println(k,v)
}
}
**自定义累加器**
class myAcc extends AccumulatorV2[String,mutable.HashMap[String,Int]]{
val countMap=new mutable.HashMap[String, Int]()
override def isZero: Boolean = countMap.isEmpty
override def copy(): AccumulatorV2[String, mutable.HashMap[String, Int]] = {
val acc = new myAcc
acc.countMap++=countMap
acc
}
override def reset(): Unit = countMap.clear()
override def add(v: String): Unit = {
// countMap(v) = countMap.getOrElse(v,0)+1
countMap+= (v->(countMap.getOrElse(v,0)+1))
}
override def merge(other: AccumulatorV2[String, mutable.HashMap[String, Int]]): Unit = {
val map: mutable.HashMap[String, Int] = other.value
//法一foldLeft
// map.foldLeft(countMap){
// case (map,(k,v))=>{
// //法一
// map+=( k-> (map.getOrElse(k,0)+v))
//
// //需要返回map
map(k) =map.getOrElse(k,0) +v
map
//
// //法三 需要返回map
map.put(k,map.getOrElse(k,0)+v)
map
// //法四
map.update(k,map.getOrElse(k,0)+v)
map
// }
// }
//法二 foreach
map.foreach{
case (k,v) =>{
countMap += (k->(countMap.getOrElse(k,0)+v))
// countMap(k) =countMap.getOrElse(k,0)+v
// countMap.update(k,countMap.getOrElse(k,0)+v)
// countMap.put(k,countMap.getOrElse(k,0)+v)
}
}
}
override def value: mutable.HashMap[String, Int] = countMap
}