• 【Spark学习笔记】广播变量和累加器


    一、广播变量(调优操作)

    使用广播变量是个调优操作,不使用广播变量可能会造成Executor端内存溢出。

    1.普通变量定义

    val rdd1: RDD[String] = sc.parallelize(Array[String]("Java", "C", "Python", "Hadoop", "Spark"),2)
    // driver每发送一个task到executor,就将此变量发送给executor一次,占用executor内存
    val blackList = List("C", "Python")
    
    val rdd2: RDD[String] = rdd1.filter(!blackList.contains(_))
    
    rdd2.foreach(println)
    

    RDD算子部分是在Executor端执行,其他的在Driver端执行。

    blackList端创建的,但是因为需要在Executor端使用,所以Driver会把blackList以task的形式发送到Executor端。如果有很多个task,Executor端就会有很多个blackList,可能会造成Executor内存溢出。

    不使用广播变量:

    img

    使用广播变量:

    img

    2.广播变量定义

    val sc = new SparkContext(conf)
    
    val rdd1: RDD[String] = sc.parallelize(Array[String]("Java", "C", "Python", "Hadoop", "Spark"),2)
    // driver每发送一个task到executor,就将此变量发送给executor一次,占用executor内存
    val blackList = List("C", "Python")
    // 使用广播变量,driver将只发送一次到executor,在executor上管理
    val broadCast = sc.broadcast(blackList)
    
    val rdd2: RDD[String] = rdd1.filter(!broadCast.value.contains(_))
    
    rdd2.foreach(println)
    

    注意:广播变量在Driver端定义赋初值,在Driver端可以修改广播变量的值,在Executor端无法修改。

    二、累加器(正确操作)

    累加器不是调优操作,而是正确操作,如果不这么做,就是错的。

    1.普通累加器

    val rdd1: RDD[String] = sc.parallelize(Array[String]("Java", "C", "Python", "Hadoop", "Spark"),2)
    // 结果为0
    var sum = 0
    // 必须得使用累加器
    val accumulator = sc.longAccumulator
    
    rdd1.foreach(_ => {
      sum += 1
      println(s"Executor -- i = $sum")
      accumulator.add(1)
      println(s"Executor -- accumulator = $accumulator")
    
    })
    println(s"sum:$sum")
    println(s"accumulator:${accumulator.value}")
    

    注意:累加器在Driver端定义赋初值,在Executor端更新。新版的Spark在Executor端也可以读取累加器的值

    2.自定义累加器

    自定义实体类,封装多个属性,实现累加

    case class PersonInfo(var personCount: Int, var ageCount: Int)
    

    自定义累加器需要继承AccumulatorV2

    class MyAcc extends AccumulatorV2[PersonInfo, PersonInfo] {
    
      private var resultInfo = new PersonInfo(0, 0)
    
      /** 判断reset 是否是初始值,一定和reset保持一致 */
      override def isZero: Boolean = {
        resultInfo.personCount == 0 && resultInfo.ageCount == 0
      }
    
      override def copy(): AccumulatorV2[PersonInfo, PersonInfo] = {
        val myAcc = new MyAcc
        myAcc.resultInfo = this.resultInfo
        myAcc
      }
    
      override def reset(): Unit = {
        resultInfo = new PersonInfo(0, 0)
      }
    
      /** 作用在Executor端 */
      override def add(v: PersonInfo): Unit = {
        resultInfo.personCount += v.personCount
        resultInfo.ageCount += v.ageCount
      }
    
      /** Driver端的result与Executor中的每个result进行聚合 */
      override def merge(other: AccumulatorV2[PersonInfo, PersonInfo]): Unit = {
        resultInfo.personCount += other.asInstanceOf[MyAcc].resultInfo.personCount
        resultInfo.ageCount += other.asInstanceOf[MyAcc].resultInfo.ageCount
      }
    
      override def value: PersonInfo = resultInfo
    }
    

    使用自定义累加器

    def main(args: Array[String]): Unit = {
    
      val conf = new SparkConf()
      conf.setMaster("local")
      conf.setAppName("自定义累加器")
    
      val sc = new SparkContext(conf)
    
      sc.setLogLevel("error")
    
      // 自定义累加器
      val myAcc = new MyAcc
      sc.register(myAcc)
    
      val rdd1: RDD[String] = sc.parallelize(Array[String](
        "张三 18", "李四 20", "王五 21", "马六 16"
      ), 3)
    
      val value: RDD[Unit] = rdd1.map(elem => {
        val age = elem.split(" ")(1).toInt
        myAcc.add(new PersonInfo(1, age))
      })
      // action算子触发
      value.count()
    
      println("accumlator value = " + myAcc.value)
    
    }
    
  • 相关阅读:
    Hadoop学习---Zookeeper+Hbase配置学习
    Hadoop学习---Hadoop的HBase的学习
    Hadoop学习---Hadoop的MapReduce的原理
    Hadoop学习---Hadoop的深入学习
    Hadoop学习---Eclipse中hadoop环境的搭建
    Hadoop学习---CentOS中hadoop伪分布式集群安装
    Hadoop学习---Ubuntu中hadoop完全分布式安装教程
    大数据学习---大数据的学习【all】
    Java实例---flappy-bird实例解析
    UML类图详细介绍
  • 原文地址:https://www.cnblogs.com/yangyh11/p/14065727.html
Copyright © 2020-2023  润新知