共享变量与broadcast join

共享变量

通常,当在远程集群节点上执行传递给Spark操作(例如mapor reduce)的函数时,它将在函数中使用的所有变量的单独副本上工作。这些变量被拷贝到每台机器上。并且远程计算机上的变量的更新不会传播回驱动程序。这样读写共享变量效率低下。但是,Spark确实为两种常见的使用模式提供了两种有限类型的共享变量:广播变量(broadcast variables)和计数器( accumulators.)。

val value = new HashMap()
val rdd = .....
rdd.foreach(x=>{
		value //..... 算子里面用到了外部value变量,那么这个value变量必须拷贝到所有task里面去
}
)

Accumulators

Accumulators are variables that are only “added” to through an associative and commutative operation and can therefore be efficiently supported in parallel. They can be used to implement counters (as in MapReduce) or sums. Spark natively supports accumulators of numeric types, and programmers can add support for new types.

As a user, you can create named or unnamed accumulators. As seen in the image below, a named accumulator (in this instance counter) will display in the web UI for the stage that modifies that accumulator. Spark displays the value for each accumulator modified by a task in the “Tasks” table.

在这里插入图片描述
可以通过分别调用SparkContext.longAccumulator()或SparkContext.doubleAccumulator() 累积Long或Double类型的值来创建数字累加器。Tasks running on a cluster can then add to it using the add method。但是,他们无法读取value。只有驱动程序可以使用value方法读取累加器的value。

下面的代码显示了一个累加器用于添加数组的元素:

scala> val accum = sc.longAccumulator("My Accumulator")
accum: org.apache.spark.util.LongAccumulator = LongAccumulator(id: 0, name: Some(My Accumulator), value: 0)

scala> sc.parallelize(Array(1, 2, 3, 4)).foreach(x => accum.add(x))
...
10/09/29 18:41:08 INFO SparkContext: Tasks finished in 0.317106 s

scala> accum.value
res2: Long = 10

也可以自定义累加器。
程序员也可以通过继承AccumulatorV2来创建自己的类型。AccumulatorV2抽象类有几个必须覆盖的方法:reset用于将累加器重置为零,add用于将另一个值添加到累加器中, merge用于将另一个相同类型的累加器合并到这个中。其他必须覆盖的方法包含在API文档中。例如,假设我们有一个MyVector表示数学向量的类,我们可以写:

class VectorAccumulatorV2 extends AccumulatorV2[MyVector, MyVector] {

  private val myVector: MyVector = MyVector.createZeroVector

  def reset(): Unit = {
    myVector.reset()
  }

  def add(v: MyVector): Unit = {
    myVector.add(v)
  }
  ...
}

// Then, create an Accumulator of this type:
val myVectorAcc = new VectorAccumulatorV2
// Then, register it into spark context:
sc.register(myVectorAcc, "MyVectorAcc1")

请注意,当程序员定义自己的AccumulatorV2类型时,结果类型可能与添加的元素类型不同。

对于仅在操作内执行的累加器更新,Spark保证每个任务对累加器的更新仅应用一次,即重新启动的任务不会更新该值。在转换中,用户应该知道,如果重新执行任务或作业阶段,则可以多次应用每个任务的更新。

累加器不会改变Spark的惰性评估模型。如果在RDD上的操作中更新它们,则只有在RDD作为操作的一部分计算时才更新它们的值。因此,当在惰性变换中进行时,不保证执行累加器更新map()。


广播变量 Broadcast Variables

广播变量允许程序员在每台机器上保留一个只读变量(一个机器一个副本),而不是一个task一个副本。Spark还尝试使用有效的广播算法来分发广播变量,以降低通信成本。
Spark actions are executed through a set of stages, separated by distributed “shuffle” operations. Spark automatically broadcasts the common data needed by tasks within each stage. The data broadcasted this way is cached in serialized form and deserialized before running each task. This means that explicitly creating broadcast variables is only useful when tasks across multiple stages need the same data or when caching the data in deserialized form is important.

Broadcast variables are created from a variable v by calling SparkContext.broadcast(v). The broadcast variable is a wrapper around v, and its value can be accessed by calling the value method. The code below shows this:

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

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

创建广播变量后,应该使用它来代替v在集群上运行的任何函数中的值,这样v就不会多次传送到节点。此外,在v广播之后不应修改对象 ,以确保所有节点获得相同的广播变量值。


!!!
当然我们现实中通常使用广播变量进行broadcast join (mapjoin)。将小表广播出去。
普通join是会引起shuffle的。
但是broadcast join不会产生shuffle,大表中的数据读取出来一跳就和广播出去的小表的记录做匹配。
可以有效减少网络传输和内存开销

val info1 = sc.parallelize(Array(("601","张三"),("602","李四"))).collectAsMap()// 通常将变量collect到driver中广播出去,并转为Map(), 到时候map.get(k)即可调用。
val info1Broadcast = sc.broadcast(info1)

val info2 = sc.parallelize(Array(("601","东南","20"),("602","科大","21"),("603","西南","23"))).map(x=>(x._1,x))

info2.mapPartitions(x=>{
	val BroadcastMap = info1Broadcast.value
	for((key,value) <- if (broadcastMap.contains(key))){
		yield ( key,broadcastMap.get(key).getOrElse(""),value._2)
}
}).foreach(println)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值