概述: 所谓共享变量,是为了解决task中使用到外部变量造成相关问题而出现的。spark提供了有限的两种共享变量:广播变量Broadcast变量和累加器Accumulator。
一、 Broadcast
1、使用说明
使用非常简单,只需要将普通的变量包装为Broadcast即可:
val xxBC:Broadcast[T] = sc.broadcast(t);
其中T是被包装的变量t的类型。
在transformation中使用的时候,只需要xxBC.value即可以获取被包装的值。
2、使用注意点
广播变量不适合处理那些大变量,还有就是不适合处理那些频繁更新的值。
3、案例
🌰:
/*
使用 Broadcast方式高效的完成类似大小表关联的操作
*/
import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkContext, SparkConf}
object BroadCast {
def main(args: Array[String]) {
val conf =new SparkConf()
.setAppName("BroadCast")
.setMaster("local[*]")
val sc = new SparkContext(conf)
//小表,学生表
val stuMap = List(
"1 zhangsan 22 bd-bj",
"2 lisi 25 bd-bj",
"3 zhangyaoshi 24 bd-sz",
"4 hudada 18 bd-wh",
"5 chenpingan 25 bd-bj"
).map(line => {
val sid = line.substring(0,1)
val info = line.substring(1).trim()
(sid,info)
}).toMap
//创建广播变量
val stuBC:Broadcast[Map[String,String]] =sc.broadcast(stuMap)
//大表,成绩表
val scores = List(
"1 1 math 82",
"2 1 english 0",
"3 2 chinese 85.5",
"4 3 PE 99",
"5 5 math 99"
)
val scoreRDD:RDD[String] = sc.parallelize(scores)
scoreRDD.map(scoreLine =>{
val filds = scoreLine.split("\\s+")
val sid = filds(1)
//取出广播变量的值
val bcMap = stuBC.value
val info = bcMap.getOrElse(sid,null)
s"${sid}\t${info}\t${filds(2)}\t${filds(3)}"
}).foreach(println)
}
}
二、Accumulator
1、使用说明
累加器的入口也是SparkContext,操作也比较简单,通过sc.accumulator(初始化的值),返回值就是一个累加器,累加器允许的操作就是++(add)。并且我们应该在driver端去获取对应的累加结果
2、使用注意点
①在指定累加器的时候可以根据需要指定一个累加器名称,方面我们可以在web-ui/4040页面查看具体信息,如果没有指定累加器名称,查看不到具体的累加信息。
②累加器的执行,必须要使用action去触发,也就是说累加器的操作必须要在transformation来累加。
③累加器值的调用,应该要在action之后。
④多次触发action操作,可能会造成累加器的多次执行,所以需要做到一旦调用完累加器做好累加器数据的重置。
3、案例
案例说明:
在将所有结果输出到文件的同时,额外的统计is和that出现了多少次,并将结果打印到控制台。
🌰:
import org.apache.spark.{SparkContext, SparkConf}
/*
在将所有结果输出到文件的同时,额外的统计is和that出现了多少次,并将结果打印到控制台。
*/
object Accumulator{
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setAppName("AccumulatorOps")
.setMaster("local[*]")
val sc = new SparkContext(conf)
val accu = sc.longAccumulator("isAccu")
val list = List("is that this is as was from where")
val lines =sc.parallelize(list)
//统计is 和that出现多少次,然后将结果打印到控制台
val pairs = lines.flatMap(_.split("\\s+"))
.map(word => {
if(word == "is" || word == "that") {
accu.add(1)
}
(word, 1)
})
val ret = pairs.reduceByKey(_+_)
println("触发之前的累加的结果:" + accu.value)
ret.foreach(println)
println("触发之后累加的结果:" + accu.value)
accu.reset()
ret.foreach(println)
println("触发之后累加的结果2:" + accu.value)
Thread.sleep(100000L)
sc.stop()
}
}
4、自定义累加器
🌰:
/*自定义累加器
AccumulatorV2[IN, OUT]
IN代表的是执行累加器add加入的指的类型
OUT代表的是执行累加器value返回值的类型
本例中IN使用String,其实代表的就是对应得要累加的字符串word
返回值,不会是每一个word的次数k-v
*/
import org.apache.spark.util.AccumulatorV2
import scala.collection.mutable
class MyAccumulator extends AccumulatorV2[String,scala.collection.mutable.Map[String,Int]]{
var map = scala.collection.mutable.Map[String, Int]()
//累加器是否有初始值
override def isZero: Boolean = true
//重置累加器
override def reset(): Unit = map.clear()
//获取累加器的值
override def value: mutable.Map[String, Int] = map
//拷贝其他task中对应累加器对应的值
override def copy(): AccumulatorV2[String, mutable.Map[String, Int]] = {
val mAccu = new MyAccumulator
mAccu.map = this.map
mAccu
}
//进行累加操作
override def add(word: String): Unit = {
// val vOption = map.get(word)
// var count = 1
/*if(vOption.isDefined) {//map中已经存在该word
map.put(word, 1 + vOption.get)
} else {//map中没有该word
map.put(word, 1)
}*/
/*if(vOption.isDefined) {
count += vOption.get
}
map.put(word, count)*/
map.put(word,map.getOrElse(word,0)+1)
}
//合并累加器的值
override def merge(other: AccumulatorV2[String, mutable.Map[String, Int]]): Unit = {
val otherMap = other.value
for((word,count) <- otherMap){
map.put(word,map.getOrElse(word,0)+count)
}
}
}
/*
使用自定义累加器
*/
import org.apache.spark.{SparkContext, SparkConf}
object UseMyAcculator {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setAppName(s"${UseMyAcculator.getClass.getSimpleName}")
.setMaster("local[2]")
val sc = new SparkContext(conf)
val list = List(
"hello you",
"i hate you",
"i miss you",
"i love you",
"get you"
)
val words = sc.parallelize(list).flatMap(_.split("\\s+"))
//实例化自定义累加器并注册
val myAccu = new MyAccumulator
sc.register(myAccu, "sparkAccu")
val pairs = words.map(word => {
if (word == "you" || word == "hate") {
myAccu.add(word)
} else {
(word, 1)
}
})
pairs.count()
println("you出现的次数:" + myAccu.value + "action执行之后")
sc.stop()
}
}