1、简介
在Spark中,提供了两种类型的共享变量:广播变量 (broadcast variable)与累加器 (accumulator) :
- 广播变量:主要用于在节点间高效分发大对象。
- 累加器:用来对信息进行聚合,主要用于累计计数等场景;
2、广播变量
广播变量:允许开发者将一个只读变量(Driver端)缓存到每个节点(Executor)上,而不是每个任务传递一个副本。
每个 Task 任务的闭包都会持有自由变量的副本,如果变量很大且 Task 任务很多的情况下,这必然会对网络 IO 造成压力,为了解决这个情况,Spark 提供了广播变量。
广播变量的做法很简单:就是不把副本变量分发到每个 Task 中,而是将其分发到每个 Executor,Executor 中的所有 Task 共享一个副本变量。
// 把一个数组定义为一个广播变量
val broadcastVar = sc.broadcast(Array(1, 2, 3, 4, 5))
// 之后用到该数组时应优先使用广播变量,而不是原值
sc.parallelize(broadcastVar.value).map(_ * 10).collect() //访问方式:broadcastVar.value
注意事项:
- 1、Driver端变量在每个Executor每个Task保存一个变量副本
- 2、Driver端广播变量在每个Executor只保存一个变量副本
import org.apache.spark.{SparkConf, SparkContext}
object BroadcastDemo {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local[1]").setAppName("BroadcastDemo")
val sc = SparkContext.getOrCreate(conf)
//广播
val arr = Array("hello","hi","good afternoon")
val hei = sc.broadcast(arr)
val rdd1 = sc.parallelize(List((1,"zhangsan"),(2,"lisi"),(3,"wangwu")))
val rdd2 = rdd1.mapValues(x => {
//使用时才传给worker
/*println(x)
println(arr.toString())
arr(0) + ":" + x*/
//广播变量,提前说
println(hei.value.toList)
hei.value(0) + ":" + x
})
rdd2.foreach(println)
}
}
/*输出:
List(hello, hi, good afternoon)
(1,hello:zhangsan)
List(hello, hi, good afternoon)
(2,hello:lisi)
List(hello, hi, good afternoon)
(3,hello:wangwu)
*/
3、累加器
原理:将每个副本变量的最终值传回 Driver,由 Driver 聚合后得到最终值,并更新原始变量。
只允许added操作,常用于实现计数。
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)
*/