Spark持久化

众所周知,RDD只会保留血缘关系,不存储计算结果。如果想要让计算结果持久化存储,那就要引入cache和persist方法。

提前感受变化

禁用持久化

package com.pzb.rdd.persist

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
 * @Description TODO
 * @author 海绵先生
 * @date 2023/4/9-16:26
 */
object Spark01_RDD_Persist {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("PersistTest")
    val sc = new SparkContext(conf)

    val rdd: RDD[String] = sc.makeRDD(List("Hello World", "Hello Spark"))
    val flatRDD: RDD[String] = rdd.flatMap(_.split(" "))
    val mapRDD: RDD[(String, Int)] = flatRDD.map(word => {
      println("@@@@@@")
      (word, 1)
    })
    // TODO 禁用持久化
    // mapRDD.cache()

    val reduceRDD: RDD[(String, Int)] = mapRDD.reduceByKey(_ + _)
    reduceRDD.collect().foreach(println)
    println("*************")
    val groupRDD: RDD[(String, Iterable[Int])] = mapRDD.groupByKey()
    groupRDD.collect().foreach(println)

    sc.stop()
    /* 整体输出结果
    @@@@@@
    @@@@@@
    @@@@@@
    @@@@@@
    (Hello,2)
    (World,1)
    (Spark,1)
    *************
    @@@@@@
    @@@@@@
    @@@@@@
    @@@@@@
    (Hello,CompactBuffer(1, 1))
    (World,CompactBuffer(1))
    (Spark,CompactBuffer(1))
     */
  }

}

启用持久化

package com.pzb.rdd.persist

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
 * @Description TODO
 * @author 海绵先生
 * @date 2023/4/9-16:26
 */
object Spark02_RDD_Persist {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("PersistTest")
    val sc = new SparkContext(conf)

    val rdd: RDD[String] = sc.makeRDD(List("Hello World", "Hello Spark"))
    val flatRDD: RDD[String] = rdd.flatMap(_.split(" "))
    val mapRDD: RDD[(String, Int)] = flatRDD.map(word => {
      println("@@@@@@")
      (word, 1)
    })
    // TODO 启用持久化
    mapRDD.cache()

    val reduceRDD: RDD[(String, Int)] = mapRDD.reduceByKey(_ + _)
    reduceRDD.collect().foreach(println)
    println("*************")
    val groupRDD: RDD[(String, Iterable[Int])] = mapRDD.groupByKey()
    groupRDD.collect().foreach(println)

    sc.stop()
    /* 整体输出结果
    @@@@@@
    @@@@@@
    @@@@@@
    @@@@@@
    (Hello,2)
    (World,1)
    (Spark,1)
    *************
    (Hello,CompactBuffer(1, 1))
    (World,CompactBuffer(1))
    (Spark,CompactBuffer(1))
     */
  }

}

可以看到:因为将中间的计算结果持久化的保存了起来,所以不再需要重新计算以前的步骤。

cache和persist方法


可以看到,cache底层调用的是persist方法

默认是写进内存的。

如果想要写到磁盘或者其他地方,需要调用persist方法。
具体存储方式有很多种:



该保存的路径都是临时路径,当程序结束后是会删除的。如果想永久保留,涉及到了checkpoint的知识了。

checkpoint

checkpoint与cache和persist的效果类似,都是将中间计算结果保存。
checkpoint与cache和persist的不同之处就在于:checkpoint在作业执行结束时不会删除保存的文件,而cache和persist会删除临时文件。

package com.pzb.rdd.persist

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
 * @Description TODO
 * @author 海绵先生
 * @date 2023/4/9-16:26
 */
object Spark03_RDD_Persist {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("PersistTest")
    val sc = new SparkContext(conf)

    val rdd: RDD[String] = sc.makeRDD(List("Hello World", "Hello Spark"))
    val flatRDD: RDD[String] = rdd.flatMap(_.split(" "))
    val mapRDD: RDD[(String, Int)] = flatRDD.map(word => {
      println("@@@@@@")
      (word, 1)
    })

    // checkpoint 需要落盘 需要通过sc.setCheckPoint()方法指定检查的的保存路径
    // 检查点路径保存的文件,当作业执行完毕后,不会被删除
    // 一般路径保存都是分布式存储文件中(HDFS),这里为方便就保存在本地吧
    sc.setCheckpointDir("cp")

    mapRDD.checkpoint()

    val reduceRDD: RDD[(String, Int)] = mapRDD.reduceByKey(_ + _)
    reduceRDD.collect().foreach(println)
    println("*************")
    val groupRDD: RDD[(String, Iterable[Int])] = mapRDD.groupByKey()
    groupRDD.collect().foreach(println)

    sc.stop()
    /* 整体输出结果
    @@@@@@
    @@@@@@
    @@@@@@
    @@@@@@
    @@@@@@
    @@@@@@
    @@@@@@
    @@@@@@
    (Hello,2)
    (World,1)
    (Spark,1)
    *************
    (Hello,CompactBuffer(1, 1))
    (World,CompactBuffer(1))
    (Spark,CompactBuffer(1))
     */
  }

}

可以看到:虽然星号下的数据没有变化,但@@@@@@输出了很多遍。
这是因为checkpoint在执行,会同时生成一个Job,重新计算一遍。
为了避免给这种情况的发生,一般checkpoint会跟cache搭配使用,让checkpoint从cache中获取数据

checkpoint结合cache使用

package com.pzb.rdd.persist

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
 * @Description TODO
 * @author 海绵先生
 * @date 2023/4/9-16:26
 */
object Spark03_RDD_Persist {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("PersistTest")
    val sc = new SparkContext(conf)

    val rdd: RDD[String] = sc.makeRDD(List("Hello World", "Hello Spark"))
    val flatRDD: RDD[String] = rdd.flatMap(_.split(" "))
    val mapRDD: RDD[(String, Int)] = flatRDD.map(word => {
      println("@@@@@@")
      (word, 1)
    })

    // checkpoint 需要落盘 需要通过sc.setCheckPoint()方法指定检查的的保存路径
    // 检查点路径保存的文件,当作业执行完毕后,不会被删除
    // 一般路径保存都是分布式存储文件中(HDFS),这里为方便就保存在本地吧
    sc.setCheckpointDir("cp")

    // checkpoint搭配cache使用,避免checkpoint从新的Job中拿数据
    mapRDD.cache()
    mapRDD.checkpoint()

    val reduceRDD: RDD[(String, Int)] = mapRDD.reduceByKey(_ + _)
    reduceRDD.collect().foreach(println)
    println("*************")
    val groupRDD: RDD[(String, Iterable[Int])] = mapRDD.groupByKey()
    groupRDD.collect().foreach(println)

    sc.stop()
    /* 整体输出结果
    @@@@@@
    @@@@@@
    @@@@@@
    @@@@@@
    (Hello,2)
    (World,1)
    (Spark,1)
    *************
    (Hello,CompactBuffer(1, 1))
    (World,CompactBuffer(1))
    (Spark,CompactBuffer(1))
     */
  }

}

三者区别?

cache :将数据临时的存储在内存中进行数据重用,会在血缘关系中添加新的依赖。一旦,出现问题,可以重头读取数据
persist :将数据临时存储在磁盘文件中进行数据重用,涉及到磁盘IO,性能较低,但是数据安全。如果作业执行完毕,临时保存的数据文件就会丢失
checkpoint :将数据长久地保存在磁盘文件中进行数据重用,涉及到醛盘IO,性能较低,但是数据交全。为了保证数据安全,所以一殷情况下,会独立执行作业为了能够提高效率,一殷情况下,是需要和cache联合使用,执行过程中,会切断血缘关系。重新建立新的血缘关系。checkpoint等同于改变数据源。

posted @ 2023-04-20 14:01  MrSponge  Views(15)  Comments(0Edit  收藏  举报