SparkCore之共享变量的解释

1. RDD1-累加器

  • 本身分布式的程序中不能直接累加的,需要借助于共享变量
  • 分布式的程序中,在Driver端定义的数值,该数值在Executor端执行真正的计算,当Executor执行完毕后,该数值不会返回得到Driver端,所以Driver拿不到该结果
  • 所以引出了在Driver端和Executor端共享的变量,在Driver端定义的数值在Executor计算完毕后,是可以直接返回结果

引出: Spark提供的Accumulator,主要用于多个节点对一个变量进行共享性的操作

  1. sc.accumulator(0)
  2. sc.longaccumulater(0) //用这个
  3. acccount.add()
  • 使用累加器: 通常在向 Spark 传递函数时,比如使用 map() 函数或者用 filter() 传条件时,可以使用驱动器driver程序中定义的变量,但是集群中运行的每个任务都会得到这些变量的一份新的副本,更新这些副本的值也不会影响驱动器Driver中的对应变量。这时使用累加器就可以实现我们想要的效果。
    val xx: Accumulator[Int] = sc.accumulator(0)

代码演示:Scala集合中

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

/**
 * @author liu a fu
 * @date 2021/1/17 0017
 * @version 1.0
 * @DESC    演示Spark的共享变量之累加器
 */
object SparkAddCumulater {
    
    

 val conf: SparkConf = new SparkConf().setAppName(this.getClass.getSimpleName.stripSuffix("$")).setMaster("local[*]")

  def main(args: Array[String]): Unit = {
    
    
    val sc = new SparkContext(conf)
    //实验1 Scala中的累加器
    val arr = Seq(1, 2, 3)
    var  count1 = 0
    arr.foreach(x => count1 += x)
    println(count1)   //6

    //实验2  RDD的累加器
    //因为定义变量在driver端定义,但是rdd的数值的计算在exector中执行的,执行完毕后没有直接返回driver端,看不到结果
    var count2=0
    val rdd1: RDD[Int] = sc.parallelize(arr)
    rdd1.foreach(x => count2 +=x)
    println(count2)   //0


    //实验3:构建在Driver端和Executor的共享的变量
    val count3: Accumulator[Int] = sc.accumulator(0) //底层实现的+=,仅仅能够在driver访问  过时
    rdd1.foreach(x => count3.add(x))
    println(count3)   //6

    //实验4:使用建议使用的方法
    //Create and register a long accumulator, which starts with 0 and accumulates inputs by `add`.  源码解释
    val count4: LongAccumulator = sc.longAccumulator("count4")
    rdd1.foreach(x => count4.add(x))
    println(count4)  //LongAccumulator(id: 51, name: Some(count4), value: 6)
    println(count4.value)  //6

  }
}

2. RDD2-广播变量

原理:
广播变量允许开发人员在每个节点(Worker or Executor)缓存只读变量,而不是在Task之间传递这些变量。使用广播变量能够高效地在集群每个节点创建大数据集的副本。同时Spark还使用高效的广播算法分发这些变量,从而减少通信的开销。
在这里插入图片描述
案例:
在这里插入图片描述
代码演示: 定义一个id+水果的Array的数据结构,这里实现通过id就能够找到水果

import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD
import org.apache.spark.{
    
    SparkConf, SparkContext}

/**
 * @author liu a fu
 * @date 2021/1/17 0017
 * @version 1.0
 * @DESC   演示广播变量
 */
object SparkBroadcast {
    
    

  def main(args: Array[String]): Unit = {
    
    
    val conf: SparkConf = new SparkConf().setAppName(this.getClass.getSimpleName.stripSuffix("$")).setMaster("local[*]")
    val sc = new SparkContext(conf)

    val kvFruit: RDD[(Int, String)] = sc.parallelize(List((1, "apple"), (2, "orange"), (3, "banana"), (4, "grape")))
    val fruitMap: collection.Map[Int, String] = kvFruit.collectAsMap()
    //这里面在数据量小的时候可以使用下面的方式
    val valueRDD: RDD[Int] = sc.parallelize(Seq(2, 1,3, 4))
    valueRDD.map(x => fruitMap(x)).collect().foreach(println(_))
    println("="*100)

    //但是如果fruitMap水果种类特别多的,每一个rdd的Task都需要拉取副本,造成大量数据网络传输,建议使用广播变量
    //这里面需要使用Map的数据结构,因为Array(1,"Apple),需要将array转化为Map结构
    val broad: Broadcast[collection.Map[Int, String]] = sc.broadcast(kvFruit.collectAsMap())
    valueRDD.map(x => broad.value(x)).collect().foreach(println(_))

    sc.stop()
  }
}

猜你喜欢

转载自blog.csdn.net/m0_49834705/article/details/112731900