spark1.X和2.X定义累加器的方式是不同的:
累加器的作用:可以实现一个变量在不同的executor端能保持状态的累加。
使用累加器的代码实现累加:
def main(args: Array[String]): Unit = {
val ssc = SparkSession.builder().appName("ac").master("local[*]").getOrCreate()
val sc = ssc.sparkContext
val arr = Array[Int](1,2,3)
val arrRDD = sc.parallelize(arr)
val acc = sc.longAccumulator("accu")
val res = arrRDD.map(t=>{
acc.add(1L)
})
res.count()
print(acc.value)
}
这里定义了一个累加器,因为arrRDD中有3的元素,所以这里的.add方法会连续调用3次,所以这里的结果就是3
不适用累加器的代码实现累加:
def main(args: Array[String]): Unit = {
val ssc = SparkSession.builder().appName("ac").master("local[*]").getOrCreate()
val sc = ssc.sparkContext
val arr = Array[Int](1,2,3)
val arrRDD = sc.parallelize(arr)
var ac = 0
val acc = sc.longAccumulator("accu")
val res = arrRDD.map(t=>{
ac+=1
})
res.count()
println(ac)
}
这里最后输入的值是0,为什么是0?主要是因为map算子中的计算过程是在executor端进行的,但是ac是在driver端定义的,所以driver端的ac是接收不到executor端计算过的ac的值。
使用累加器注意的事项:
防止重复累加:
def main(args: Array[String]): Unit = {
val ssc = SparkSession.builder().appName("ac").master("local[*]").getOrCreate()
val sc = ssc.sparkContext
val arr = Array[Int](1,2,3)
val arrRDD = sc.parallelize(arr)
var ac = 0
val acc = sc.longAccumulator("accu")
val res = arrRDD.map(t=>{
acc.add(1L)
})
res.count()
println(acc.value)
res.collect()
print(acc.value)
}
结果为 3和6,也就是这里出现了重复累加的操作,主要是因为这里连续两次调用了action算子,所以这里累加器进行了两次重复的累加,也就是说,累加器实在遇到action算子的时候才进行累加操作的。
正确的写法:在action之前加上cache操作
def main(args: Array[String]): Unit = {
val ssc = SparkSession.builder().appName("ac").master("local[*]").getOrCreate()
val sc = ssc.sparkContext
val arr = Array[Int](1,2,3)
val arrRDD = sc.parallelize(arr)
var ac = 0
val acc = sc.longAccumulator("accu")
val res = arrRDD.map(t=>{
acc.add(1L)
})
res.cache().count()
println(acc.value)
res.collect()
print(acc.value)
}