在Spark中,提供了两种类型的共享变量:广播变量 (broadcast variable)与累加器 (accumulator)
广播变量:允许开发者将一个只读变量(Driver端)缓存到每个节点(Executor)上,而不是每个任务传递一个副本。
每个 Task 任务的闭包都会持有自由变量的副本,如果变量很大且 Task 任务很多的情况下,这必然会对网络 IO 造成压力,为了解决这个情况,Spark 提供了广播变量。
广播变量的做法很简单:就是不把副本变量分发到每个 Task 中,而是将其分发到每个 Executor,Executor 中的所有 Task 共享一个副本变量。
注意事项:
scala> val arr = Array("hello","hi","good afternoon")
val arr: Array[String] = Array(hello, hi, good afternoon)
scala> val rdd1 = sc.parallelize(List((1,"zhangsan"),(2,"lisi"),(3,"wangwu")))
val rdd1: org.apache.spark.rdd.RDD[(Int, String)] = ParallelCollectionRDD[0] at parallelize at :1
scala> val hei = sc.broadcast(arr)
val hei: org.apache.spark.broadcast.Broadcast[Array[String]] = Broadcast(0)
scala> val rdd2 = rdd1.mapValues(x => {
| //使用时才传给worker
| /*println(x)
| println(arr.toString())
| arr(0) + ":" + x*/
|
| //广播变量,提前说
| println(hei.value.toList)
| hei.value(0) + ":" + x
| })
val rdd2: org.apache.spark.rdd.RDD[(Int, String)] = MapPartitionsRDD[1] at mapValues at :1
scala> rdd2.foreach(println)
List(hello, hi, good afternoon)
(2,hello:lisi)
List(hello, hi, good afternoon)
(3,hello:wangwu)
List(hello, hi, good afternoon)
(1,hello:zhangsan)
scala> val rdd2 = rdd1.mapValues(x => {
| //使用时才传给worker
| println(x)
| println(arr.toString())
| arr(0) + ":" + x
| })
val rdd2: org.apache.spark.rdd.RDD[(Int, String)] = MapPartitionsRDD[2] at mapValues at :1
scala> rdd2.foreach(println)
zhangsan
[Ljava.lang.String;@7ac9ee7
(1,hello:zhangsan)
lisi
[Ljava.lang.String;@5dcdd8ae
(2,hello:lisi)
wangwu
[Ljava.lang.String;@5dcdd8ae
(3,hello:wangwu)
原理:将每个副本变量的最终值传回 Driver,由 Driver 聚合后得到最终值,并更新原始变量。
只允许add操作,常用于实现计数。
import org.apache.spark.{SparkConf, SparkContext}
object AccumulatorDemo {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local[2]").setAppName("AccumulatorDemo")
val sc = SparkContext.getOrCreate(conf)
val accum = sc.accumulator(0,"aaa") //第一个参数:初始值 第二个参数:name
sc.parallelize(Array(1,2,3,4)).foreach(x=>{accum+=x;accum})
println(accum.value)
//拓展:glom 打印分区
val rdd1 = sc.makeRDD(List(1,2,3,4))
rdd1.glom().collect().foreach(x=>println(x.toList))
}
}
/*输出:
10
List(1, 2)
List(3, 4)
*/