广播变量 broadcast variable
1、广播变量的意义
如果我们要在分布式计算里面分发大对象,例如:字典,集合,黑白名单等,这个都会由Driver端进行分发,一般来讲,如果这个变量不是广播变量,那么每个task就会分发一份,这在task数目十分多的情况下Driver的带宽会成为系统的瓶颈,而且会大量消耗task服务器上的资源,如果将这个变量声明为广播变量,那么只是每个executor拥有一份,这个executor启动的task会共享这个变量,节省了通信的成本和服务器的资源。
如:一个spark应用有50个executor,1000个tasks,一个10M大小的数据,不使用广播变量,则需要10M*1000 = 10G的内存,而使用广播变量则只需要10M * 50 = 500M内存
2、代码样例
package com.test.bigdata
import org.apache.spark.{SparkConf, SparkContext}
object BroadcastApp {
def main(args: Array[String]) {
val sparkConf = new SparkConf()
.setMaster("local[2]").setAppName("SparkContextApp")
val sc = new SparkContext(sparkConf)
// commonJoin(sc)
broadcastJoin(sc)
sc.stop()
}
def broadcastJoin(sc: SparkContext): Unit = {
// 假设 a1是个小表
val a1 = sc.parallelize(Array(("1", "大米"), ("2", "土豆"), ("29", "小花"))).collectAsMap()
//广播
val a1Broadcast = sc.broadcast(a1)
sc.longAccumulator("").add(1)
val f11 = sc.parallelize(Array(("29", "深圳", 18), ("10", "北京", 2)))
.map(x => (x._1, x))
f11.mapPartitions(partition => {
// 获取广播里面的内容
val a1Stus = a1Broadcast.value
for ((key, value) <- partition if (a1Stus.contains(key)))
yield (key, a1Stus.getOrElse(key,""), value._2, value._3)
})
}
def commonJoin(sc: SparkContext): Unit = {
// a1 join f11 on a1.id = f11.id ==> 29,"小花","深圳",18
val a1 = sc.parallelize(Array(("1", "大米"), ("2", "土豆"), ("29", "小花"))).map(x => (x._1, x))
val f11 = sc.parallelize(Array(("29", "深圳", 18), ("10", "北京", 2))).map(x => (x._1, x))
a1.join(f11).map(x => {
x._1 + " , " + x._2._1._2 + " , " + x._2._2._2 + " , " + x._2._2._3
}).collect()
}
}
3、注意事项
- 广播变量不能过大
- 广播变量是只读属性,不能修改,在Driver端可以修改广播变量的值,重新进行广播,在Executor端无法修改广播变量的值。
- 必须将RDD进行action操作之后在进行广播
val a1 = sc.parallelize(Array((“1”, “大米”), (“2”, “土豆”), (“29”, “小花”))).collectAsMap()
val a1Broadcast = sc.broadcast(a1) //广播
累加器 accumulator
1、累加器
在spark应用程序中,我们经常会有这样的需求,如异常监控,调试,记录符合某特性的数据的数目,这种需求都需要用到计数器,如果一个变量不被声明为一个累加器,那么它将在被改变时不会再driver端进行全局汇总,即在分布式运行时每个task运行的只是原始变量的一个副本,并不能改变原始变量的值,但是当这个变量被声明为累加器后,该变量就会有分布式计数的功能。
2、累加器的使用
val conf = new SparkConf()
conf.setMaster("local").setAppName("accumulator")
val sc = new SparkContext(conf)
//定义累加器
val accumulator = sc.accumulator(0)
//分布式累加
sc.textFile("./words.txt").foreach { x =>{accumulator.add(1)}}
//获取累加器的结果
println(accumulator.value)
sc.stop()