- cache :将数据临时存储在内存中进行数据重用,如果作业执行完毕,临时保存的数据文件就会丢失
- persist :将数据临时存储在磁盘文件中进行数据重用,涉及到磁盘I0,性能较低,但是数据安全,如果作业执行完毕,临时保存的数据文件就会丢失
- checkpoint :将数据长久地保存在磁盘文件中进行数据重用,涉及到磁盘Io,性能较低,但是数据安全。为了保证数据安全,所以一般情况下,会独立执行作业,所以会有重复操作,为了优化性能,往往和cache一起使用。
RDD结构本身不记录数据,只记录操作,所以简单对RDD的复用并不是说这个操作只执行一次,真实还是会执行两次,如下面的代码可以看出,两个action触发了转换函数的两次执行,即他们的lineage血脉分别单独执行了一次。
object SparkWordCount2 {
def main(args: Array[String]): Unit = {
var sparkConf= new SparkConf().setMaster("local").setAppName("WordCount")
var sc = new SparkContext(sparkConf)
val rdd: RDD[String] = sc.makeRDD(List("hello word, hello spark"))
val wordRDD:RDD[String] = rdd.flatMap(_.split(" "))
val wordToOneRDD: RDD[(String, Int)] = wordRDD.map(iter=>{
println("转换函术执行了") //总共4个单词,所以map操作一次共打印4个
(iter,1)})
val wordToCountRDD1: RDD[(String, Int)] =wordToOneRDD.reduceByKey((_+_)) //两个不同的action 操作会分别从源头rdd执行转换
val wordToCountRDD2: RDD[(String, Iterable[Int])] = wordToOneRDD.groupByKey(2) //两个不同的action 操作会分别从源头rdd执行转换
wordToCountRDD1.collect().foreach(println)
wordToCountRDD2.collect().foreach(println)
sc.stop()
}
}
输出:
转换函术执行了
转换函术执行了
转换函术执行了
转换函术执行了
(spark,1)
(word,1)
(hello,2)
转换函术执行了
转换函术执行了
转换函术执行了
转换函术执行了
(hello,CompactBuffer(1, 1))
(word,CompactBuffer(1))
(spark,CompactBuffer(1))
Process finished with exit code 0
要想实现复用,或者避免重复进行很长的转换函数,可以使用cache或persist缓存数据,例如在上面出现宽依赖之前使用,即
object SparkWordCount2 {
def main(args: Array[String]): Unit = {
var sparkConf= new SparkConf().setMaster("local").setAppName("WordCount")
var sc = new SparkContext(sparkConf)
val rdd: RDD[String] = sc.makeRDD(List("hello word, hello spark"))
val wordRDD:RDD[String] = rdd.flatMap(_.split(" "))
val wordToOneRDD: RDD[(String, Int)] = wordRDD.map(iter=>{
println("转换函术执行了") //总共4个单词,所以map操作一次共打印4个
(iter,1)})
// wordToOneRDD.persist()
wordToOneRDD.cache()
val wordToCountRDD1: RDD[(String, Int)] =wordToOneRDD.reduceByKey((_+_)) //两个不同的action 操作会分别从源头rdd执行转换
val wordToCountRDD2: RDD[(String, Iterable[Int])] = wordToOneRDD.groupByKey(2) //两个不同的action 操作会分别从源头rdd执行转换
wordToCountRDD1.collect().foreach(println)
wordToCountRDD2.collect().foreach(println)
sc.stop()
}
}
这样最终map函数只会执行一次,输出如下:
转换函术执行了
转换函术执行了
转换函术执行了
转换函术执行了
(spark,1)
(word,,1)
(hello,2)
(word,,CompactBuffer(1))
(hello,CompactBuffer(1, 1))
(spark,CompactBuffer(1))
Process finished with exit code 0
但是persist或cache的数据只是临时的,job执行完后会失效,(所以也不需传入保存的路径参数)。
为了持久保存,可以使用检查点checkpoint,使用时还应和cache等配合使用,因为为了最求可靠,checkpoint会对每个job执行一遍(每个action对应一个job)。(一般保存在HDFS等分布式文件系统上,要传入相应保存路径)
使用时代码如下:
object SparkWordCount2 {
def main(args: Array[String]): Unit = {
var sparkConf= new SparkConf().setMaster("local").setAppName("WordCount")
var sc = new SparkContext(sparkConf)
val rdd: RDD[String] = sc.makeRDD(List("hello word, hello spark"))
val wordRDD:RDD[String] = rdd.flatMap(_.split(" "))
val wordToOneRDD: RDD[(String, Int)] = wordRDD.map(iter=>{
println("转换函术执行了") //总共4个单词,所以map操作一次共打印4个
(iter,1)})
// wordToOneRDD.persist()
wordToOneRDD.cache()
wordToOneRDD.checkpoint("path on hdfs")//填入对应分布式文件系统上保存的路径
val wordToCountRDD1: RDD[(String, Int)] =wordToOneRDD.reduceByKey((_+_)) //两个不同的action 操作会分别从源头rdd执行转换
val wordToCountRDD2: RDD[(String, Iterable[Int])] = wordToOneRDD.groupByKey(2) //两个不同的action 操作会分别从源头rdd执行转换
wordToCountRDD1.collect().foreach(println)
wordToCountRDD2.collect().foreach(println)
sc.stop()
}
}