Spark累加器

Accumulator(累加器, 计数器)
类似于MapReduce中的counter, 将数据从一个节点发送到其他各个节
点上去;

通常用于监控, 调试, 记录符合某类特征的数据数目等

累加器在Driver端被读取,使用的是 Accumulator.value

累加器在Executor端被读取,使用的是 Accumulator.localValue,获取的是Executor本地的值。Executor端可以通过 Accumulator+=1的方式进行累加(Spark1.6方式)

 

 创建累加器对累加器进行累加Driver端获取累加器Executor端获取累加器
Spark1.6sc.accumulator(0L,"counter0")counter1 += 1println("counter1="+counter1) println("counter1="+counter1.localValue)
Spark2.1sc.longAccumulator("total_counter")total_counter.add(1)println("total_counter="+total_counter.value) println("total_counter="+total_counter.value)

 

累加器的使用陷阱:

我们都知道,spark中的一系列transform操作会构成一串长的任务链,此时需要通过一个action操作来触发,accumulator也是一样。因此在一个action操作之前,你调用value方法查看其数值,肯定是没有任何变化的。

如果程序中有两次 action操作,就会触发两次transform操作,相应地,累加器就会加两次。问题代码如下:

 

val accum= sc.accumulator(0, "Error Accumulator")
val data = sc.parallelize(1 to 10)
//用accumulator统计偶数出现的次数,同时偶数返回0,奇数返回1
val newData = data.map{x => {
  if(x%2 == 0){
    accum += 1
      0
    }else 1
}}
//使用action操作触发执行
newData.count
//此时accum的值为5,是我们要的结果
accum.value

//继续操作,查看刚才变动的数据,foreach也是action操作
newData.foreach(println)
//上个步骤没有进行累计器操作,可是累加器此时的结果已经是10了
//这并不是我们想要的结果
accum.value

 

 

 

累加器陷阱解决办法:

将任务之间的依赖关系切断,再次执行action操作就可以了

 

//
val accum= sc.accumulator(0, "Error Accumulator")
val data = sc.parallelize(1 to 10)

//代码和上方相同
val newData = data.map{x => {...}}
//使用cache缓存数据,切断依赖。
newData.cache.count
//此时accum的值为5
accum.value

newData.foreach(println)
//此时的accum依旧是5
accum.value

 

调用cache,persist方法后,被persist的RDD的action操作只会使这个RDD计算一次,所以后续的累加器就不会再被之前的transfrom操作影响到了。

更新于2019/4/22

使用 RDD的localCheckpoint 方法也可以解决累加器陷阱,因为 localCheckpoint() 调用了 persist 方法。而调用RDD的checkpoint方法(内部没有调用persist方法),并不能解决这个问题。可见,调用persist方法才能解决累加器陷阱。

spark-shell中的代码是基于Spark2.3.3的

scala> val accum=sc.longAccumulator("accum")
accum: org.apache.spark.util.LongAccumulator = LongAccumulator(id: 0, name: Some(accum), value: 0)

scala> val newData=sc.parallelize(1 to 10).map(x=>{if(x%2==0) {accum.add(1);0;} else 1;})
newData: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[1] at map at <console>:26

scala> val newDataCache = newData.cache
newDataCache: newData.type = MapPartitionsRDD[1] at map at <console>:26

scala> accum.value
res0: Long = 0

scala> newDataCache.count
res1: Long = 10

scala> accum.value
res2: Long = 5

scala> newDataCache.count
res3: Long = 10

scala> accum.value
res4: Long = 5

scala> newData.count
res5: Long = 10

scala> accum.value
res6: Long = 5

scala> val newData2=sc.parallelize(1 to 10).map(x=>{if(x%2==0) {accum.add(1);0;} else 1;})
newData2: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[3] at map at <console>:26

scala> newData2.localCheckpoint
res7: newData2.type = MapPartitionsRDD[3] at map at <console>:26

scala> accum.value
res8: Long = 5

scala> newData2.count
res10: Long = 10

scala> accum.value
res11: Long = 10

scala> newData2.count
res12: Long = 10

scala> accum.value
res13: Long = 10
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值