共享變量分爲兩種:Broadcast Variable(廣播變量) 和 Accumulator(累加變量)
什麼是共享變量?
默認情況下,如果在一個算子的函數中使用到了某個外部的變量,那麼這個變量的值會被拷貝到每個task中。此時每個task只能操作自己的那份變量副本。如果多個task想要共享某個變量,那麼這種方式是做不到的。
Braadcast Variable會將使用到的變量,僅僅爲每個節點拷貝一份,更大的用處是優化性能,減少網絡傳輸以及內存消耗。
Accumulator則可以讓多個task共同操作一份變量,主要是進行累加操作。
Broadcast Variable(廣播變量的創建):
spark提供的broadcast Variable,是隻讀的。並且在每個節點上只會有一份副本,而不會爲每個task都拷貝一份副本。因此其最大作用,就是減少變量到各個節點的網絡傳輸消耗,以及在各個節點上的內存消耗。此外,spark自己內部也使用了高效的廣播算法來減少網絡消耗。
可以通過調用SparkContext的broadcast()方法,來針對某個變量創建廣播變量。然後在算子的函數中,使用到該變量的時候,每個節點只會拷貝一份副本。每個節點可以使用廣播變量的value()方法獲取值。 切記,廣播變量是只讀的。
val factor = 3
val factorBroadcast = sc.broadcast(factor)
val arr = Array(1, 2, 3, 4, 5)
val rdd = sc.parallelize(arr)
val multipleRdd = rdd.map(num => num * factorBroadcast.value())
multipleRdd.foreach(num => println(num))
package spark.stady.two;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.api.java.function.VoidFunction;
import org.apache.spark.broadcast.Broadcast;
import java.util.Arrays;
import java.util.List;
/**
* ClassName BroadcastVariable
* Description 廣播變量
* @author Vincent
* @version 1.0
* @data 2019/1/22 14:56
*/
public class BroadcastVariable {
public static void main(String[] args) {
//1.創建sparkConf
SparkConf conf = new SparkConf()
.setAppName("BroadcastVariable")
.setMaster("local");
//2.創建JavaSparkContext來接收conf。
JavaSparkContext sc = new JavaSparkContext(conf);
//3.定義一個Broadcast Variable。
final int factor = 3;
//創建廣播變量。
final Broadcast<Integer> factorBroadcast = sc.broadcast(factor);
//4.定義一個list作爲我們的RDD。
List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
//5.調用我們的sparkcontext的parallelize方法將爲我們的list創建並行的RDD。
JavaRDD<Integer> numbers = sc.parallelize(numberList);
//6、讓RDD中的每一個數字,都進行一個 乘以外部的factor操作!
//注意這裏面的我們沒有使用到barocast Variable。
JavaRDD<Integer> multipleNumber = numbers.map(new Function<Integer, Integer>() {
@Override
public Integer call(Integer v1) throws Exception {
return v1 * factor;
}
});
multipleNumber.foreach(new VoidFunction<Integer>() {
@Override
public void call(Integer value) throws Exception {
System.out.println(value);
}
});
//這是我們要使用broadcast variable 的方式創建廣播變量。
// 在第三步通過調用sc的broadcast方法來創建廣播變量。
JavaRDD<Integer> broadcastMultipleNumber = numbers.map(new Function<Integer, Integer>() {
@Override
public Integer call(Integer v1) throws Exception {
//使用共享變量,調用其value()方法,即可獲取其內部封裝的值。
int factor = factorBroadcast.value();
return factor * v1;
}
});
broadcastMultipleNumber.foreach(new VoidFunction<Integer>() {
@Override
public void call(Integer value) throws Exception {
System.out.println("broadcast variable :" + value);
}
});
sc.close();
}
}
上面是一個JAVA版本的案例。
Spark的還有一個Accumulator 變量:主要用於多個節點對一個變量進行共享性的操作。Accumulater只提供了累加的功能。但是卻給我們提供了多個task對一個變量並行操作的功能。但是task只能對Accumulator進行累加操作,不能讀取它的值。只有Driver程序可以讀取Accumulator的值。
package spark.stady.two;
import org.apache.spark.Accumulator;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.VoidFunction;
import java.util.Arrays;
import java.util.List;
/**
* ClassName Accumulator
* Description 累加變量
* @author Vincent
* @version 1.0
* @data 2019/1/23 15:10
*/
public class AccumulatorVariable {
public static void main(String[] args) {
SparkConf conf = new SparkConf()
.setAppName("AccumulatorVariable")
.setMaster("local");
JavaSparkContext sc = new JavaSparkContext(conf);
//創建Accumulator變量
//需要調用sparkcontext的accumulator()方法
final Accumulator<Integer> accumulatorsum = sc.accumulator(0);
List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6);
JavaRDD<Integer> numbers = sc.parallelize(numberList);
numbers.foreach(new VoidFunction<Integer>() {
@Override
public void call(Integer value) throws Exception {
//然後在函數內部,就可以使用accumulator變量,調用add()方法,累加求和
accumulatorsum.add(value);
}
});
//在driver程序中,可以調用accumulator的value()方法,獲取其值
System.out.println(accumulatorsum.value());
sc.close();
}
}
這是我們使用SparkContext的accumulator()方法,來實現的一個共享變量的操作,其中調用了Accumulator的add()方法進行累加,調用了Accumulator的value()方法獲取值,但是在獲取值的時候我們是在driver中獲取的。