Spark_RDD累加器和广播变量

RDD累加器和广播变量

在默认情况下,当Spark在集群的多个不同节点的多个任务上并行运行一个函数时,它会把函数中涉及到的每个变量,在每个任务上都生成一个副本。但是,有时候需要在多个任务之间共享变量,或者在任务(Task)和任务控制节点(Driver Program)之间共享变量。

为了满足这种需求,Spark提供了两种类型的变量:

1.累加器accumulators:累加器支持在所有不同节点之间进行累加计算(比如计数或者求和)

2.广播变量broadcast variables:广播变量用来把变量在所有节点的内存之间进行共享,在每个机器上缓存一个只读的变量,而不是为机器上的每个任务都生成一个副本。

累加器

不使用累加器
var counter=0
val data=List(1,2,3)
val rdd = sc.parallelize(data)
rdd.foreach(x=>counter+=x)
//结果为:counter:6
println("counter:"+counter)
 
var counter=0
val conf = new SparkConf().setAppName("tt").setMaster("local[*]")
val sc = new SparkContext(conf)
val data=List(1,2,3)
val rdd = sc.parallelize(data)
rdd.foreach(x=>counter+=x)
//结果为:counter:0
println("counter:"+counter)
使用累加器

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

val xx: Accumulator[Int] = sc.accumulator(0)

上代码:


```bash

```bash

```bash

```bash
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("wc").setMaster("local[*]")
    val sc = new SparkContext(conf)
    sc.setLogLevel("warn")
 
    //使用scala集合累加
    var counter1: Int = 0;
    var data = Seq(1,2,3)
    data.foreach(x => counter1 += x )
    println(counter1)//6
 
    println("-------------------------------------")
 
    //使用rdd累加,未使用累加器
    var counter2: Int = 0;
    val dataRDD= sc.parallelize(data) //分布式集合的[1,2,3]
    dataRDD.foreach(x => counter2 += x)
    println(counter2)//0
    //RDD操作运行结果是0
    //因为foreach中的函数是传递给Worker中的Executor执行,用到了counter2变量
    //而counter2变量在Driver端定义的,在传递给Executor的时候,各个Executor都有了一份counter2
    //最后各个Executor将各自个x加到自己的counter2上面了,和Driver端的counter2没有关系
 
    println("-------------------------------------")
 
    //使用累加器
    val counter3 = sc.accumulator(0)
    dataRDD.foreach(x=>counter3+=x)
    println(counter3)//6
    
  }
 

广播变量

不使用广播变量

在这里插入图片描述

使用广播变量

在这里插入图片描述

上代码演示

def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("wc").setMaster("local[*]")
    val sc = new SparkContext(conf)
    //不适用广播变量
    val add = sc.parallelize(List((1,"apple"),(2,"orange"),(3,"banana"),(4,"grape")))
    val fruitMap = add.collectAsMap()
    val fruitIds = sc.parallelize(List(2,4,1,3))
    //根据水果编号取水果名称
    val fruitNames = fruitIds.map(x=>fruitMap(x))
    fruitNames.foreach(println)
    //注意:以上代码看似一点问题没有,但是考虑到数据量如果较大,且Task数较多,
    //那么会导致,被各个Task共用到的fruitMap会被多次传输
    //应该要减少fruitMap的传输,一台机器上一个,被该台机器中的Task共用即可
    //如何做到?---使用广播变量
    println("----------------------------------")
    val broadcastFruitMap = sc.broadcast(fruitMap)
    val fruitname2 = fruitIds.map(x=>broadcastFruitMap.value(x))
    fruitname2.foreach(println)
  }
 
发布了126 篇原创文章 · 获赞 154 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/weixin_45737446/article/details/105574956