Spark非常重要的一个功能特性就是可以将RDD 持久化在内存中,当对RDD执行持久化操作时,每个节点都会将自己操作的RDD的partition持久化到内存中,并且在之后对该RDD的反复使用中,直接使用内存缓存的partition,这样的话,对于针对一个RDD反复执行多个操作的场景,就只要对RDD计算一次即可,后面直接使用该RDD ,而不需要计算多次该RDD
巧妙使用RDD持久化,甚至在某些场景下,可以将spark应用程序的性能提升10倍。对于迭代式算法和快速交互式应用来说,RDD持久化,是非常重要的。
要持久化一个RDD,只要调用其cache()或者persist()方法即可。在该RDD第一次被计算出来时,就会直接缓存在每个节点中。而且Spark的持久化机制还是自动容错的,如果持久化的RDD的任何partition丢失了,那么Spark会自动通过其源RDD,使用transformation操作重新计算该partition。
cache()和persist()的区别在于,cache()是persist()的一种简化方式,cache()的底层就是调用的persist()的无参版本,同时就是调用persist(MEMORY_ONLY),将数据持久化到内存中。如果需要从内存中去除缓存,那么可以使用unpersist()方法。
Spark自己也会在shuffle操作时,进行数据的持久化,比如写入磁盘,主要是为了在节点失败时,毕淼需要重新计算的过程。
Persist.java
package com.starmcu.git.spark.java;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
/**
* RDD持久化
*
*/
public class Persist {
public static void main(String[] args) {
System.out.println("妈蛋");
SparkConf conf =new SparkConf().setAppName("Persist").setMaster("local");
JavaSparkContext sc =new JavaSparkContext(conf);
//cache()或者persist()的使用,时候规则的
//必须在transformation或者textFile等创建了一个RDD之后,直接连续调用cache()或persist()才可以
//如果你先创建一个RDD,然后单独另起一行执行cache()或persist()是没有用的
//而且,会报错,大量的文件会丢失
JavaRDD lines =sc.textFile("C://1.txt").cache();
long time1 =System.currentTimeMillis();
long count =lines.count();
long time2 =System.currentTimeMillis();
System.out.println(time2-time1+"!");
long count2 =lines.count();
long time3 =System.currentTimeMillis();
System.out.println(time3-time2+"!!");
sc.close();
}
}
共享变量
通常情况下,当向Spark操作(如map,reduce)传递一个函数时,它会在一个远程集群节点上执行,它会使用函数中所有变量的副本。这些变量被复制到所有的机器上,远程机器上并没有被更新的变量会向驱动程序回传。在任务之间使用通用的,支持读写的共享变量是低效的。 尽管如此,Spark提供了两种有限类型的共享变量,广播变量和累加器。
AccumulatorTest.scala
package com.starmcu.git.spark.scala
import org.apache.spark.{SparkConf, SparkContext}
object AccumulatorTest {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("AccumulatorTest").setMaster("local")
val sc =new SparkContext(conf)
val sum =sc.accumulator(0)
val numberArray =Array(1,2,3,4,56)
val numbers =sc.parallelize(numberArray,1)
numbers.foreach(num => sum +=num)
print(sum.value)
}
}
AccumulatorTest.java
package com.starmcu.git.spark.java;
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;
/**
* 累加变量
*
*
*/
public class AccumulatorTest {
public static void main(String[] args) {
SparkConf conf =new SparkConf().setAppName("Accumulator").setMaster("local");
JavaSparkContext sc =new JavaSparkContext(conf);
Accumulator sum =sc.accumulator(0);
List numberList = Arrays.asList(1,2,3,4,6);
JavaRDD numbers =sc.parallelize(numberList);
numbers.foreach(new VoidFunction() {
@Override
public void call(Integer integer) throws Exception {
//然后在函数内部,就可以对Accumulator变量,调用add()方法,累加值
sum.add(integer);
}
});
//在driver程序中,可以调用Accumulator的value()方法,获取其值
System.out.println(sum.value());
sc.close();
}
}
广播变量
Spark的另一种共享变量是广播变量。通常情况下,当一个RDD的很多操作都需要使用driver中定义的变量时,每次操作,driver都要把变量发送给worker节点一次,如果这个变量中的数据很大的话,会产生很高的传输负载,导致执行效率降低。使用广播变量可以使程序高效地将一个很大的只读数据发送给多个worker节点,而且对每个worker节点只需要传输一次,每次操作时executor可以直接获取本地保存的数据副本,不需要多次传输。
BroadcastVariable.scala
package com.starmcu.git.spark.scala
import org.apache.spark.{SparkConf, SparkContext}
object BroadcastVariable {
def main(args: Array[String]): Unit = {
val conf =new SparkConf().setMaster("local").setAppName("BroadcastVariable")
val sc = new SparkContext(conf)
val factor =3
val broadcastVariable = sc.broadcast(factor)
val numberArray = Array(1,2,3,45,6)
val numbers = sc.parallelize(numberArray,1)
val multipleNumber =numbers.map(num => num *broadcastVariable.value)
multipleNumber.foreach(num => print(num+"---"))
}
}
BroadcastVariable.java
package com.starmcu.git.spark.java;
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;
/**
*
* 广播变量
*
*/
public class BroadcastVariable {
public static void main(String[] args) {
SparkConf conf =new SparkConf().setMaster("local").setAppName("BroadcastVariable");
JavaSparkContext sc =new JavaSparkContext(conf);
//在java中,创建共享变量,就是调用SparkContext的Broadcast方法
//获取的返回结果是Broadcast(T)类型
final int factor =3;
final Broadcast factorBroadcast =sc.broadcast(factor);
List numberList = Arrays.asList(1,24,5,7,8,90,3);
JavaRDD numbers = sc.parallelize(numberList);
//让集合中的每个数字,都乘以外部定义的那个factor
JavaRDD numbersMap = numbers.map(new Function() {
@Override
public Integer call(Integer v1) throws Exception {
//使用共享变量时,调用其value()方法,即可获取其内部分装的值
int factor =factorBroadcast.value();
return v1 * factor;
}
});
numbersMap.foreach(new VoidFunction() {
@Override
public void call(Integer integer) throws Exception {
System.out.println(integer);
}
});
sc.close();
}
}