Spark系列--SparkCore(七)广播变量和累加器

一、广播变量

  通常情况下,当一个RDD的很多操作都需要使用driver中定义的变量时,每次操作,driver都要把变量发送给worker节点一次,如果这个变量中的数据很大的话,会产生很高的传输负载,导致执行效率降低。

  使用广播变量可以使程序高效地将一个很大的只读数据发送给多个worker节点,而且对每个worker节点只需要传输一次,每次操作时executor可以直接获取本地保存的数据副本,不需要多次传输。广播变量用来高效分发较大的对象。比如,如果你的应用需要向所有节点发送一个较大的只读查询表,甚至是机器学习算法中的一个很大的特征向量,广播变量用起来都很顺手。

val broadcastList = sc.broadcast(List("Spark","Impala","Hadoop"))

sc.parallelize(List("1","2","3")).map(x => broadcastList.value ++ x).collect

广播变量的优势:是因为不是每个task一份变量副本,而是变成每个节点的executor才一份副本。这样的话,就可以让变量产生的副本大大减少。

原理:

广播变量,初始的时候,就在Drvier上有一份副本。task在运行的时候,想要使用广播变量中的数据,此时首先会在自己本地的Executor对应的BlockManager中,尝试获取变量副本;如果本地没有,BlockManager,也许会从远程的Driver上面去获取变量副本;也有可能从距离比较近的其他节点的Executor的BlockManager上去获取,并保存在本地的BlockManager中;BlockManager负责管理某个Executor对应的内存和磁盘上的数据,此后这个executor上的task,都会直接使用本地的BlockManager中的副本。

例如:
50个executor,1000个task。一个map,10M:

默认情况下,1000个task,1000份副本。10G的数据,网络传输,在集群中,耗费10G的内存资源。

如果使用了广播变量。50个execurtor,50个副本。500M的数据,网络传输,而且不一定都是从Driver传输到每个节点,还可能是就近从最近的节点的executor的bockmanager上拉取变量副本,网络传输速度大大增加;500M,大大降低了内存消耗。


二、累加器

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

使用累加器可以很简便地对各个worker返回给driver的值进行聚合。只有driver能获取到Accumulator的值(使用value方法),Task只能对其做增加操作(使用 +=)。

import org.apache.spark.{SparkConf, SparkContext}

object AccumulatorOperator {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local").setAppName("accumulator")
    val sc = new SparkContext(conf)
    //累加器的初始值为0
    val accumulator = sc.accumulator(0)
    sc.textFile("./records.txt",2).foreach {
      //两个变量
      x =>{accumulator.add(1)
      println(accumulator)
      }
    println(accumulator.value)
    sc.stop()
  }
}

从结果来看,累加器会先计算各分区的计数,然后再把各分区的计数累加

注:有需要的话,还可以自定义累加器

你可能感兴趣的:(Spark)