如何重启spark_Spark 分布式共享变量

一、闭包

1、什么是闭包,闭包的本质是什么?

闭包的本质是一个函数,在Scala中函数是一个特殊的类型FunctionX。

闭包就是一个封闭的作用域, 也是一个对象。

如下closure返回的函数areaFunction就是一个闭包

 def closure(): Int => Double = {
    val factor = 3.14
    val areaFunction = (r: Int) => {
      math.pow(r, 2) * factor
    }
    areaFunction
  }

2、spark如何分发闭包?

如下flatMap中传入的是另外一个函数, 传入的这个函数就是一个闭包, 这个闭包会被序列化运行在不同的 Executor 中

val value = 3
sc.textFile("dataset/access_log_sample.txt")
  .flatMap(item => item + value)
  .collect()

问题:如果有10个Task,value就会被分发十份,就会有10个value,但是如果集群中只有2个Executor,无论有多少Task,都会被放在这2个Executor上执行。

解决:使用广播变量

  • 在使用广播之前,复制value数和Task数量一致
  • 在使用广播之后,复制次数和Executor数量一致。

Spark 对共享变量也提供了两种支持:(1)累加器 (2)广播变量

二、累加器:(解决了共享变量写的问题)

1)特性:

  • 累加器能保证在 Spark 任务出现问题被重启的时候不会出现重复计算
  • 累加器只有在 Action 执行的时候才会被触发

2)使用方式:

  • 只有一个方法:acc.add()
  • Driver 端调用value来获取数值:acc.value

3)使用场景:

  • 需要对共享变量进行修改
  • 对同一个rdd需要遍历多次计算多个指标

1、系统自带累加器:

  • sc.longAccumulator
  • sc.doubleAccumulator
  • sc.collectionAccumulator
object AccumulatorDemo {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("AccumulatorDemo").setMaster("local[2]")
    val sc = new SparkContext(conf)
    sc.setLogLevel("WARN")

    val numlist = List(30, 50, 70, 60, 10, 20)
    val numrdd = sc.parallelize(numlist, 2)

    val acc = sc.longAccumulator
    val accresultrdd = numrdd.map(x => {
      acc.add(1)
      x
    })
    accresultrdd.collect
    println(acc.value)

    sc.stop()
  }

}

2、自定义int累加器:

/**
 * AccumulatorV2[Int, Int]泛型说明
 *  a. 对什么值进行累加
 *  b. 累加器最终的值
 */
class CustomIntAccumulator extends AccumulatorV2[Int, Int] {
  private var sum = 0

  //对缓冲区的值进行判"零"
  override def isZero: Boolean = sum == 0

  //复制累加器(把当前的累加器复制为一个新的累加器)
  override def copy(): AccumulatorV2[Int, Int] = {
    val newIntAcc = new CustomIntAccumulator
    newIntAcc.sum = sum
    newIntAcc
  }

  //重置累加器(把缓冲区的值重置为"零")
  override def reset(): Unit = sum = 0

  //真正的累加方法(分区内的累加)
  override def add(v: Int): Unit = sum += v

  //分区间的合并(把other的sum合并到this的sum中)
  override def merge(other: AccumulatorV2[Int, Int]): Unit = other match {
    case acc: CustomIntAccumulator => this.sum += acc.sum
    case _ => this.sum += 0
  }

  //返回累加后的最终值
  override def value: Int = sum
}

//使用自定义累加器
object MyIntAccDemo {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("MyIntAccDemo").setMaster("local[2]")
    val sc = new SparkContext(conf)
    sc.setLogLevel("WARN")

    val numlist = List(30, 50, 70, 60, 10, 20)
    val numrdd = sc.parallelize(numlist, 2)

    //先注册自定义累加器
    val customIntAccumulator = new CustomIntAccumulator
    sc.register(customIntAccumulator,"intAcc")

    val resultrdd = numrdd.map(x =>{
      customIntAccumulator.add(1)
      x
    })
    resultrdd.collect
    println(customIntAccumulator.value)
    
    sc.stop()
  }
}

3、自定义map累加器

/**
 * 累加器的值同时包含sum,count,avg
 * Map("sum" -> 1000,"count" -> 10,"avg" -> 100)
 */
class CustomMapAccumulator extends AccumulatorV2[Double, Map[String, Any]] {
  private var map = Map[String, Any]()

  override def isZero: Boolean = map.isEmpty

  override def copy(): AccumulatorV2[Double, Map[String, Any]] = {
    val newMapAcc = new CustomMapAccumulator
    newMapAcc.map = map
    newMapAcc
  }

  //不可变集合, 直接赋值一个空的集合
  override def reset(): Unit = Map[String, Any]()

  // 对sum和count进行累加. avg在最后value()函数中进行计算
  override def add(v: Double): Unit = {
    map += "sum" -> (map.getOrElse("sum", 0D).asInstanceOf[Double] + v)
    map += "count" -> (map.getOrElse("count", 0L).asInstanceOf[Long] + 1L)
  }

  // 合并两个map
  override def merge(other: AccumulatorV2[Double, Map[String, Any]]): Unit = {
    other match {
      case mapAcc: CustomMapAccumulator =>
        map +=
          "sum" -> (map.getOrElse("sum", 0D).asInstanceOf[Double] + mapAcc.map.getOrElse("sum", 0D).asInstanceOf[Double])
        map +=
          "count" -> (map.getOrElse("count", 0L).asInstanceOf[Long] + mapAcc.map.getOrElse("count", 0L).asInstanceOf[Long])
      case _ => throw new UnsupportedOperationException
    }
  }

  override def value: Map[String, Any] = {
    map += "avg" -> (map.getOrElse("sum", 0D).asInstanceOf[Double] / map.getOrElse("count", 0L).asInstanceOf[Long])
    map
  }
}

三、广播变量(解决共享变量读的问题)

1、解决大变量读的问题

广播变量将一个 Read-Only 的变量缓存到集群中每个executor, 而不是传递给每个 Task,executor下的所有task可以共享这个大变量,减少内存占用
每一个Executor对应一个BlockManager,广播变量存储在BlockManager内存中。

914f8c3d01571ed5ad3021658436d2bb.png

2、广播变量API

id唯一标识
value广播变量的值
unpersist在 Executor 中异步的删除缓存副本
destroy销毁所有此广播变量所关联的数据和元数据
toString字符串表示

3、使用广播变量

//数组中存在的元素保留,不存在过滤
object BroadCastDemo {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setAppName("BroadCastDemo").setMaster("local[2]")
    val sc: SparkContext = new SparkContext(conf)

    val bigArr = 1 to 1000 toArray
    // 广播变量
    val broadcastArr = sc.broadcast(bigArr)

    val listnum = List(30, 50000000, 70, 600000, 10, 20)
    val listrdd  = sc.parallelize(listnum, 4)

    val resultrdd = listrdd.filter(x => broadcastArr.value.contains(x))
    resultrdd.collect.foreach(println)

    Thread.sleep(1000000)
    sc.stop()
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值