Spark -- 累加器和广播变量

Spark – 累加器和广播变量

文章目录

    • Spark -- 累加器和广播变量
      • 一. 累加器
        • 1. 系统累加器
        • 2. 自定义累加器
    • 二. 广播变量

spark有三大数据结构:

RDD:分布式弹性数据集

累加器:分布式只写数据集

广播变量:分布式只读数据集

一. 累加器

累加器用来对信息进行聚合,通常在向 Spark传递函数时,比如使用 map() 函数或者用 filter() 传条件时,可以使用驱动器程序中定义的变量,但是集群中运行的每个任务都会得到这些变量的一份新的副本,更新这些副本的值也不会影响驱动器中的对应变量。如果我们想实现所有分片处理时更新共享变量的功能,那么累加器可以实现我们想要的效果。

1. 系统累加器

通过在驱动器中调用SparkContext.accumulator(initialValue)方法,创建出存有初始值的累加器。返回值为 org.apache.spark.Accumulator[T] 对象,其中 T 是初始值 initialValue 的类型。Spark闭包里的执行器代码可以使用累加器的 += 方法(在Java中是 add)增加累加器的值。 驱动器程序可以调用累加器的value属性(在Java中使用value()或setValue())来访问累加器的值。

注意:工作节点上的任务不能访问累加器的值。从这些任务的角度来看,累加器是一个只写变量。

对于要在行动操作中使用的累加器,Spark只会把每个任务对各累加器的修改应用一次。因此,如果想要一个无论在失败还是重复计算时都绝对可靠的累加器,我们必须把它放在 foreach() 这样的行动操作中。转化操作中累加器可能会发生不止一次更新

def main(args: Array[String]): Unit = {
    //得到spark的上下文对象
    val conf:SparkConf=new SparkConf().setMaster("local[*]").setAppName("makeRDD")
    val sc = new SparkContext(conf)

    val dataRDD: RDD[Int] = sc.makeRDD(List(1,2,3,4,5,5,5,6))

    //创建累加器对象
    val accumulator: LongAccumulator = new LongAccumulator
    //或者使用sc的longAccumulator方法来创建累加器对象
    // val accumulator: LongAccumulator = sc.longAccumulator()

    dataRDD.foreach{
      case i =>{
        //使用累加器,此方法无返回值
        accumulator.add(i)
      }
    }
    //获取累加器的值
    println(accumulator.value)

    sc.stop()
  }
2. 自定义累加器

自定义累加器步骤:

1.创建一个类继承AccumulatorV2[参数1,参数2],参数1:driver输入类型,excutor输出类型

2.重写方法

3.new 出自定义的累加器

4.sparkcontext调用register()进行注册

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

object Spark_accumulattor {
  def main(args: Array[String]): Unit = {
    //得到spark的上下文对象
    val conf:SparkConf=new SparkConf().setMaster("local[*]").setAppName("makeRDD")
    val sc = new SparkContext(conf)

    val dataRDD: RDD[Int] = sc.makeRDD(List(1,2,3,4,5,5,5,6))

    //TOOO 创建累加器
    val accumulator: WordAccumulator1 = new WordAccumulator1
    //注册累加器,让spark知道累加器的存在
    sc.register(accumulator)
    dataRDD.foreach{
      case i =>{
        accumulator.add(i.toString)
      }
    }
    println(accumulator)
  }
}

//声明累加器
//1.继承AccumulatorV2
//2.实现抽象方法
//3.创建累加器
class WordAccumulator1 extends  AccumulatorV2[String,java.util.ArrayList[String]]{

  val list= new util.ArrayList[String]()

  //当时的累加器是不是初始化状态
  override def isZero: Boolean = {
    list.isEmpty
  }

  //复制累加器对象
  override def copy(): AccumulatorV2[String, util.ArrayList[String]] ={
    new WordAccumulator1
  }

  //重置累加器对象
  override def reset(): Unit = {
    //清空集合
    list.clear()
  }
  //向累加器中增加数据
  override def add(v: String): Unit = {
    if (v!=0){
      list.add(v)
    }
  }
  //合并
  override def merge(other: AccumulatorV2[String, util.ArrayList[String]]): Unit = {
    list.addAll(other.value)
  }

  override def value: util.ArrayList[String] = {
    list
  }
}

二. 广播变量

广播变量用来高效分发较大的对象。向所有工作节点发送一个较大的只读值,以供一个或多个Spark操作使用。比如,如果你的应用需要向所有节点发送一个较大的只读查询表,甚至是机器学习算法中的一个很大的特征向量,广播变量用起来都很顺手。 在多个并行操作中使用同一个变量,但是 Spark会为每个任务分别发送。

scala> val broadcastVar = sc.broadcast(Array(1, 2, 3))
broadcastVar: org.apache.spark.broadcast.Broadcast[Array[Int]] = Broadcast(35)


scala> broadcastVar.value
res33: Array[Int] = Array(1, 2, 3)

使用广播变量的过程如下:

(1) 通过对一个类型 T 的对象调用 SparkContext.broadcast 创建出一个 Broadcast[T] 对象。 任何可序列化的类型都可以这么实现。

(2) 通过 value 属性访问该对象的值(在 Java 中为 value() 方法)。

(3) 变量只会被发到各个节点一次,应作为只读值处理(修改这个值不会影响到别的节点)。

你可能感兴趣的:(Spark,spark)