RDD的持久化
其实就是对RDD的数据进行缓存处理,为什么用到缓存?
在RDD得到执行过程中不会保存数据,只会保存血缘关系(依赖关系),那么如果一个RDD被多个RDD依赖就会出现,一个依赖之后数据就没了,另一个还需根据血缘关系去找到最初数据重新走一遍这是非常效率低下的。所以引出了缓存来解决。
1)RDD cache缓存
RDD通过Cache 或者 Persist 方法将前面的计算结果缓存,默认情况下会把数据以缓存在 JVM的堆内存中。但是并不是这两个方法被调用时立即缓存,而是触发后面的action算子时,该RDD将会被缓存在计算节点的内存中,并供后面重用。
cache 操作会增加血缘关系,不改变原有的血缘关系
persist可以更改存储级别
2)RDD checkpoint检查点
通过将RDD结果写入磁盘来实现缓存 。
对RDD进行 checkpoint 操作并不会马上被执行,必须执行Action 操作才能触发
缓存和检查点区别
1.Cache 缓存只是将数据保存起来,不切断血缘依赖。Checkpoint 检查点切断血缘依赖
2.Cache 缓存的数据通常存储在磁盘、内存等地方,可靠性低。Checkpoint 的数据通常存 储在HDFS 等容错、高可用的文件系统,可靠性高
3.建议对 checkpoint()的RDD使用Cache 缓存,这样 checkpoint 的 job 只需从 Cache 缓存 中读取数据即可,否则需要再从头计算一次RDD
object WCall {
def main(args: Array[String]): Unit = {
val sparkContext = new SparkContext(new SparkConf().setMaster("local[*]").setAppName("WCall"))
sparkContext.setCheckpointDir("cp")
val rdd: RDD[String] = sparkContext.makeRDD(List(
"hello", "spark", "scala", "spark"
))
// (spark,CompactBuffer(spark, spark))
// (scala,CompactBuffer(scala))
// (hello,CompactBuffer(hello))
val mapRDD: RDD[(String, Int)] = rdd.map(word => {
// 通过它来标识执行次数,从而判断数据是否从头来过
println("-----")
(word,1)
})
// 打印依赖关系
println(mapRDD.toDebugString)
// 缓存
mapRDD.cache()
// mapRDD.checkpoint()
// 缓存之后的依赖关系
println(mapRDD.toDebugString)
// 1、
val value: RDD[(String, Int)] = mapRDD.reduceByKey(_ + _)
value.collect().foreach(println)
// 2、
val value1: RDD[(String, Iterable[Int])] = mapRDD.groupByKey().mapValues(iter =>List(iter.size))
value1.collect().foreach(println)
sparkContext.stop()
}
}
无缓存结果:
cache结果:
checkpoint结果:
这里要注意的是checkpoint在提交job才会执行(执行行动算子),所以要看checkpoint之后的血缘关系要在提交job后
cache 机制是每计算出一个要 cache 的 partition 就直接将其 cache 到内存了。但 checkpoint 没有使用这种第一次计算得到就存储的方法,而是等到 job 结束后另外启动专门的 job 去完成 checkpoint 。也就是说需要 checkpoint 的 RDD 会被计算两次
因此一般情况下都是cache和checkpoint结合使用