11_spark_core_累加器_系统累加器

11_spark_core_累加器_系统累加器

累加器

累加器:分布式共享只写变量。(Executor和Executor之间不能读数据)
累加器用来把Executor端变量信息聚合到Driver端。在Driver中定义的一个变量,在Executor端的每个task都会得到这个变量的一份新的副本,每个task更新这些副本的值后,传回Driver端进行合并计算。
在这里插入图片描述

系统累加器

1)累加器使用
(1)累加器定义(SparkContext.accumulator(initialValue)方法)
val sum: LongAccumulator = sc.longAccumulator(“sum”)
(2)累加器添加数据(累加器.add方法)
sum.add(count)
(3)累加器获取数据(累加器.value)
sum.value
2)创建包名:com.atguigu.accumulator

需求

  val dataRDD: RDD[(String, Int)] = sc.makeRDD(List(("a", 1), ("a", 2), ("a", 3), ("a", 4)))
    //需求:统计a出现的所有次数 ("a",10)

3)代码实现

package com.atguigu.accumulator

import org.apache.spark.rdd.RDD
import org.apache.spark.util.LongAccumulator
import org.apache.spark.{SparkConf, SparkContext}

/**
 * @author dxy
 * @date 2021/2/25 14:18
 */
object accumulator01_system {
  def main(args: Array[String]): Unit = {
    //TODO 1.创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //TODO 2.创建SparkContext,该对象是提交Spark App的入口
    val sc: SparkContext = new SparkContext(conf)

    val dataRDD: RDD[(String, Int)] = sc.makeRDD(List(("a", 1), ("a", 2), ("a", 3), ("a", 4)))
    //需求:统计a出现的所有次数 ("a",10)

    //1.普通算子实现,底层会走shfffle,效率低
    val rdd: RDD[(String, Int)] = dataRDD.reduceByKey(_+_)

    //rdd.collect().foreach(println)

    //2.普通变量实现
    //普通变量没法实现需求,因为普通变量只能从driver端传到executor端,不能从executor端返回到driver端

/*    var sum:Int=0

    dataRDD.foreach{
      case (a,cnt)=>{
        sum +=cnt
        println("sum = "+ sum)
      }
    }

    println("a",sum)*/

    //3.累加器实现
    //3.1 创建累加器
    val accSum: LongAccumulator = sc.longAccumulator("sum")

    //3.2使用累加器
    dataRDD.foreach{
      case (a,cnt)=>{
        accSum.add(cnt)
        //3.4 在excutor端读取的累加器的值,不是最终的值,不对  因此我们累加器为分布式共享只写变量
        println("accSum = "+accSum.value)
      }
    }

    //3.3获取累加器的值
    println(accSum.value)



    //TODO 3.关闭连接
    sc.stop()

  }
}

结果:

accSum = 4
accSum = 3
accSum = 2
accSum = 1
10

注意:Executor端的任务不能读取累加器的值(例如:在Executor端调用sum.value,获取的值不是累加器最终的值)。因此我们说,累加器是一个分布式共享只写变量
3)累加器要放在行动算子中
因为转换算子执行的次数取决于job的数量,如果一个spark应用有多个行动算子,那么转换算子中的累加器可能会发生不止一次更新,导致结果错误。所以,如果想要一个无论在失败还是重复计算时都绝对可靠的累加器,我们必须把它放在foreach()这样的行动算子中。
对于在行动算子中使用的累加器,Spark只会把每个Job对各累加器的修改应用一次。

object accumulator02_updateCount {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")
    val sc = new SparkContext(conf)

    val dataRDD: RDD[(String, Int)] = sc.makeRDD(List(("a", 1), ("a", 2), ("a", 3), ("a", 4)))
    //需求:统计a出现的所有次数 ("a",10)
    //累加器实现
    //1 声明累加器
    val accSum: LongAccumulator = sc.longAccumulator("sum")

    val mapRDD: RDD[Unit] = dataRDD.map {
      case (a, count) => {
        //2 使用累加器累加  累加器.add()
        accSum.add(count)
        // 4 不要在executor端获取累加器的值,因为不准确 因此我们说累加器叫分布式共享只写变量
        //println("sum = " + accSum.value)
      }
    }

    //调用两次行动算子,map执行两次,导致最终累加器的值翻倍
    mapRDD.collect()
    mapRDD.collect()

    /**
     * 结论:使用累加器最好要在行动算子中使用,因为行动算子只会执行一次,而转换算子的执行次数不确定!
     */ 
    //2 获取累加器的值 累加器.value
    println(("a",accSum.value))
    
    sc.stop()
  }
}

运行结果:

accSum = 2
accSum = 3
accSum = 1
accSum = 4
accSum = 2
accSum = 3
accSum = 4
accSum = 1
20

总结

1.普通变量在driver初始化=>executor端,不能从executor端返回driver端
2.spark有一个自带累加器sc.longAccumulator可以实现excutor端=>driver
3.spark自带累加器使用步骤:
	第一步:创建累加器
	val accSum: LongAccumulator = sc.longAccumulator("sum")
	sum名字而已:最好见名知意
	第二步:使用累加器
	 dataRDD.foreach{
      case (a,cnt)=>{
        accSum.add(cnt)
        //3.4 在excutor端读取的累加器的值,不是最终的值,不对  因此我们累加器为分布式共享只写变量
        println("accSum = "+accSum.value)
      }
    }
	第三步:获取累加器的值
	println(accsum.value)

4.使用累加器最好要在行动算子中使用,因为行动算子只会执行一次,而转换算子的执行次数不确定!
	因为行动算子执行RDD从头开始,没加缓存的话
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值