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)

上代碼:

  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)
  }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章