引言
要实现同一个rdd的分组聚合操作和分组操作
通过以下代码会发现代码的重复率很高,性能很低
如果一个RDD需要重复使用,那么需要从头再次执行来获取数据
RDD对象可以重用,但是数据无法重用
这时候我们就可以思考如何能够使RDD的数据也能够重用呢?
object Spark_rdd_01 {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("RDD").setMaster("local[*]")
val sc = new SparkContext(conf)
val rdd: RDD[String] = sc.makeRDD(List("hello spark","hello scala"))
val flatRDD: RDD[String] = rdd.flatMap(_.split(" "))
val mapRDD: RDD[(String, Int)] = flatRDD.map((_,1))
val reduceRDD: RDD[(String, Int)] = mapRDD.reduceByKey(_+_)
reduceRDD.collect().foreach(println)
println("----------------------")
val rdd1: RDD[String] = sc.makeRDD(List("hello spark","hello scala"))
val flatRDD1: RDD[String] = rdd1.flatMap(_.split(" "))
val mapRDD1: RDD[(String, Int)] = flatRDD1.map((_,1))
val groupRDD1: RDD[(String, Iterable[Int])] = mapRDD1.groupByKey()
groupRDD1.collect().foreach(println)
sc.stop()
}
}
(spark,1)
(scala,1)
(hello,2)
----------------------
(spark,CompactBuffer(1))
(scala,CompactBuffer(1))
(hello,CompactBuffer(1, 1))
一. RDD Cache缓存
RDD 通过Cache 或者 Persist 方法将前面的计算结果缓存,默认情况下会把数据以缓存在 JVM 的堆内存中。但是并不是这两个方法被调用时立即缓存,而是触发后面的 action 算子时,该RDD 将会被缓存在计算节点的内存中,并供后面重用。 所以持久化操作必须在行动算子执行时完成的。
RDD对象的持久化操作不一定是为了重用,在数据执行较长,或数据比较重要的场合也可以采用持久化操作
cache默认持久化的操作,底层调用的是Persist的MEMORY_ONLY,只能将数据保存到内存中,如果想要保存到磁盘文件,需要更改存储的级别
未设置缓存
object Spark_rdd_01 {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("RDD").setMaster("local[*]")
val sc = new SparkContext(conf)
val rdd: RDD[String] = sc.makeRDD(List("hello spark","hello scala"))
val flatRDD: RDD[String] = rdd.flatMap(_.split(" "))
val mapRDD: RDD[(String, Int)] = flatRDD.map(x=>{
println("map执行一次")
(x,1)
})
val reduceRDD: RDD[(String, Int)] = mapRDD.reduceByKey(_+_)
reduceRDD.collect().foreach(println)
println("----------------------")
val groupRDD1: RDD[(String, Iterable[Int])] = mapRDD.groupByKey()
groupRDD1.collect().foreach(println)
sc.stop()
}
}
map执行一次
map执行一次
map执行一次
map执行一次
(spark,1)
(scala,1)
(hello,2)
----------------------
map执行一次
map执行一次
map执行一次
map执行一次
(spark,CompactBuffer(1))
(scala,CompactBuffer(1))
(hello,CompactBuffer(1, 1))
设置缓存
object Spark_rdd_01 {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("RDD").setMaster("local[*]")
val sc = new SparkContext(conf)
val rdd: RDD[String] = sc.makeRDD(List("hello spark","hello scala"))
val flatRDD: RDD[String] = rdd.flatMap(_.split(" "))
val mapRDD: RDD[(String, Int)] = flatRDD.map(x=>{
println("map执行一次")
(x,1)
})
//设置了缓存
//mapRDD.cache()
mapRDD.persist(StorageLevel.DISK_ONLY)
val reduceRDD: RDD[(String, Int)] = mapRDD.reduceByKey(_+_)
reduceRDD.collect().foreach(println)
println("----------------------")
val groupRDD1: RDD[(String, Iterable[Int])] = mapRDD.groupByKey()
groupRDD1.collect().foreach(println)
sc.stop()
}
}
map执行一次
map执行一次
map执行一次
map执行一次
(spark,1)
(scala,1)
(hello,2)
----------------------
(spark,CompactBuffer(1))
(scala,CompactBuffer(1))
(hello,CompactBuffer(1, 1))
二. RDD CheckPoint检查点
所谓的检查点其实就是通过将RDD 中间结果写入磁盘 由于血缘依赖过长会造成容错成本过高,这样就不如在中间阶段做检查点容错,如果检查点之后有节点出现问题,可以从检查点开始重做血缘,减少了开销。
对 RDD 进行 checkpoint 操作并不会马上被执行,必须执行 Action 操作才能触发。
object Spark_rdd_01 {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("RDD").setMaster("local[*]")
val sc = new SparkContext(conf)
sc.setCheckpointDir("datas\\checkpoint")
val rdd: RDD[String] = sc.makeRDD(List("hello spark","hello scala"),1)
val flatRDD: RDD[String] = rdd.flatMap(_.split(" "))
val mapRDD: RDD[(String, Int)] = flatRDD.map(x=>{
println("map执行一次")
(x,1)
})
//checkpoint需要落盘,需要指定检查点保存路径
//检查点路径保存的文件,当作业执行完毕后,不会被删除
//一般保存路径都是在分布式存储系统:HDFS
mapRDD.checkpoint()
val reduceRDD: RDD[(String, Int)] = mapRDD.reduceByKey(_+_)
reduceRDD.collect().foreach(println)
println("----------------------")
val groupRDD1: RDD[(String, Iterable[Int])] = mapRDD.groupByKey()
groupRDD1.collect().foreach(println)
sc.stop()
}
}
map执行一次
map执行一次
map执行一次
map执行一次
map执行一次
map执行一次
map执行一次
map执行一次
(scala,1)
(spark,1)
(hello,2)
----------------------
(scala,CompactBuffer(1))
(spark,CompactBuffer(1))
(hello,CompactBuffer(1, 1))
三. 缓存和检查点区别
- cache和persist只是将数据保存起来,不切断血缘依赖,会在血缘关系中添加新的依赖,一旦出现问题,可以从头读取数据,而Checkpoint 检查点会切断血缘依赖,重新建立新的血缘关系,checkpoint等同于改变数据源
- 缓存的数据通常存储在磁盘、内存等地方,可靠性低,如果作业执行完毕,临时保存的数据文件就会丢失,Checkpoint 的数据通常存储在HDFS等高容错、高可用的文件系统,涉及到磁盘IO,性能较低,但数据安全,可靠性高。
- 建议对checkpoint()的RDD 使用Cache 缓存,和cache联合使用,这样 checkpoint 的 job 只需从 Cache缓存中读取数据即可,否则需要再从头计算一次RDD。
object Spark_rdd_01 {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("RDD").setMaster("local[*]")
val sc = new SparkContext(conf)
sc.setCheckpointDir("datas\\checkpoint")
val rdd: RDD[String] = sc.makeRDD(List("hello spark","hello scala"),1)
val flatRDD: RDD[String] = rdd.flatMap(_.split(" "))
val mapRDD: RDD[(String, Int)] = flatRDD.map(x=>{
println("map执行一次")
(x,1)
})
mapRDD.cache()
mapRDD.checkpoint()
val reduceRDD: RDD[(String, Int)] = mapRDD.reduceByKey(_+_)
reduceRDD.collect().foreach(println)
println("----------------------")
val groupRDD1: RDD[(String, Iterable[Int])] = mapRDD.groupByKey()
groupRDD1.collect().foreach(println)
sc.stop()
}
}
map执行一次
map执行一次
map执行一次
map执行一次
(scala,1)
(spark,1)
(hello,2)
----------------------
(scala,CompactBuffer(1))
(spark,CompactBuffer(1))
(hello,CompactBuffer(1, 1))