1. 概述
默认情况下,若在一个算子函数中使用到了来自外部的某个变量,则该变量的值会被拷贝到每个Task中,此时每个Task只能操作自己获得的副本,而无法实现多个Task间的共享。
Spark为此提供了两种共享变量,一种是Broadcast Variable(广播变量),另一种是Accumulator(累加变量):
(1)Broadcast Variable将被使用到的变量,仅为 “每个节点” 拷贝一份,减少每个Task拷贝副本的网络传输及内存消耗,优化性能。
(2)Accumulator可以让多个Task共同操作一份变量,实现“并发写”,主要进行累加。
2. Broadcast Variable
广播变量是只可读不可写的,每个节点上只会有一份副本,而不会为每个Task都拷贝一份副本。 因此,减少了变量至各个节点的网络传输消耗以及在各个节点上的内存消耗。此外,Spark在内部使用了高效的广播算法来减少网络消耗。
通过SparkContext.broadcast(variable),来创建对应variable的广播变量,然后在算子中使用到广播变量时,每个节点只会拷贝一份副本。每个节点可以使用广播变量的value()进行取值。
(1)java版本
/**
* 广播变量
* @author Z -Jay
*
*/
public class BroadcastVariable {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("BroadcastVariable" ).setMaster("local");
JavaSparkContext sc = new JavaSparkContext(conf );
Integer factor = 3;
//Java中创建factor的共享广播变量,返回Broadcast<T>,每个节点拷贝一份
final Broadcast<Integer> broadcastFactor = sc.broadcast(factor );
List<Integer> numList = Arrays.asList(1,2,3,4,5);
JavaRDD<Integer> javaRDD = sc .parallelize(numList);
//让RDD中的每个数字与外部定义的factor相乘
JavaRDD<Integer> resRDD = javaRDD .map(new Function<Integer, Integer>() {
private static final long serialVersionUID = 1L;
@Override
public Integer call(Integer num ) throws Exception {
// 获取该节点共享变量的值
Integer factor = broadcastFactor .value();
return num *factor ;
}
});
resRDD.foreach(new VoidFunction<Integer>() {
private static final long serialVersionUID = 1L;
@Override
public void call(Integer num) throws Exception {
System. out.println(num );
}
});
sc.close();
}
}
(2)scala版本
def main(args:Array[String]){
val conf =new SparkConf().setAppName ("BroadcastVariable" ).setMaster ("local" )
val sc = new SparkContext(conf )
val factor = 3
val broadcastFactor = sc. broadcast(factor)
val numArray = Array(1,2,3,4,5)
val srcRDD = sc.parallelize(numArray, 5)
val multiRDD = srcRDD.map{ num => num*broadcastFactor.value }
multiRDD.foreach{ num => println(num) }
}
3. Accumulator
累加变量主要用于多个Task对一个变量 “并发写”,但Task只能进行累加操作,不能读取它的值,只有Driver节点的程序可以读取Accumulator的值。
通过SparkContext.accumulator(variable),来创建variable的广播变量。
/**
* 累加变量
* @author Z -Jay
*
*/
public class AccumulatorVariable {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("AccumulatorVariable" ).setMaster("local");
JavaSparkContext sc = new JavaSparkContext(conf);
//Java中创建共享累加变量,返回Accumulator<T>
Accumulator<Integer> sum = sc .accumulator (0);
List<Integer> numList = Arrays.asList(1,2,3,4,5);
JavaRDD<Integer> javaRDD = sc .parallelize(numList);
javaRDD.foreach(new VoidFunction<Integer>() {
private static final long serialVersionUID = 1L;
@Override
public void call(Integer val) throws Exception {
//在算子函数内使用Accumulator.add()累加
sum.add(val);
}
});
//在Driver程序中调用Accumulator.val()获取其值
System. out.println(sum .value ());
sc.close();
}
}