spark_RDD持久化,检查点、缓存

298 阅读5分钟

1. RDD Cache缓存

RDD通过Cache或者Persist方法将前面的计算结果缓存,默认情况下会把数据以序列化的形式缓存在JVM的堆内存中。但是并不是这两个方法被调用时立即缓存,而是触发后面的action时,该RDD将会被缓存在计算节点的内存中,并供后面重用。

  • cache在底层调用的还是Persist方法
def cache(): this.type = persist()
def persist(): this.type = persist(StorageLevel.MEMORY_ONLY)
  • 注意:虽然叫持久化,但是当应用程序程序执行结束之后,缓存的目录也会被删除
  • 实际场景下,我们一般不会持久化到磁盘上,性能是一方面,另外一方面,因为是分布式的,有可能持久化到某个节点,那么无法保证其它节点也会同样持久化。

缓存实例:

object Spark01_cache {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交Spark App的入口
    val sc: SparkContext = new SparkContext(conf)

    //创建RDD
    val rdd: RDD[String] = sc.makeRDD(List("hello bangzhang","hello jingjing"),2)

    //扁平映射
    val flatMapRDD: RDD[String] = rdd.flatMap(_.split(" "))

    //结构转换
    val mapRDD: RDD[(String, Int)] = flatMapRDD.map {
      word => {
        println("********************")
        (word, 1)
      }
    }

    //打印血缘关系
    println(mapRDD.toDebugString)

    //对RDD的数据进行缓存   底层调用的是persist函数   默认缓存在内存中
    mapRDD.cache()

    //persist可以接收参数,指定缓存位置
    //注意:虽然叫持久化,但是当应用程序程序执行结束之后,缓存的目录也会被删除
    //mapRDD.persist()
    //mapRDD.persist(StorageLevel.DISK_ONLY)

    //触发行动操作
    mapRDD.collect()
    println("---------------------------------------")
    //打印血缘关系
    println(mapRDD.toDebugString)
    //触发行动操作
    mapRDD.collect()
//    Thread.sleep(1000000000);
    // 关闭连接
    sc.stop()
  }
}

缓存级别

mapRdd.cache()
def cache(): this.type = persist()
def persist(): this.type = persist(StorageLevel.MEMORY_ONLY)

object StorageLevel {
  val NONE = new StorageLevel(false, false, false, false)
  val DISK_ONLY = new StorageLevel(true, false, false, false)
  val DISK_ONLY_2 = new StorageLevel(true, false, false, false, 2)
  val MEMORY_ONLY = new StorageLevel(false, true, false, true)
  val MEMORY_ONLY_2 = new StorageLevel(false, true, false, true, 2)
  val MEMORY_ONLY_SER = new StorageLevel(false, true, false, false)
  val MEMORY_ONLY_SER_2 = new StorageLevel(false, true, false, false, 2)
  val MEMORY_AND_DISK = new StorageLevel(true, true, false, true)
  val MEMORY_AND_DISK_2 = new StorageLevel(true, true, false, true, 2)
  val MEMORY_AND_DISK_SER = new StorageLevel(true, true, false, false)
  val MEMORY_AND_DISK_SER_2 = new StorageLevel(true, true, false, false, 2)
  val OFF_HEAP = new StorageLevel(true, true, true, false, 1)

注意:默认的存储级别都是仅在内存存储一份。在存储级别的末尾加上“_2”表示持久化的数据存为两份。 缓存有可能丢失,或者存储于内存的数据由于内存不足而被删除,RDD的缓存容错机制保证了即使缓存丢失也能保证计算的正确执行。通过基于RDD的一系列转换,丢失的数据会被重算,由于RDD的各个Partition是相对独立的,因此只需要计算丢失的部分即可,并不需要重算全部Partition。

自带缓存的算子:

Spark会自动对一些Shuffle操作的中间数据做持久化操作(比如:reduceByKey)。这样做的目的是为了当一个节点Shuffle失败了避免重新计算整个输入。但是,在实际使用的时候,如果想重用数据,仍然建议调用persist或cache。

2. RDD CheckPoint检查点

  1. 检查点:是通过将RDD中间结果写入磁盘。
  2. 为什么要做检查点?
由于血缘依赖过长,造成容错代价过大(相当于从头再来,成本自然高了),这样就不如在中间
阶段做检查点容错,如果检查点之后节点出现问题,直接从检查点开始重做血缘,减少开销。
  1. 检查点的数据存放?
CheckPoint的数据一般存放在HDFS等容错,高可用的文件系统上
  1. 检查点数据存储格式?
二进制文件
  1. 检查点切断血缘:
在Checkpoint的过程中,该RDD的所有依赖于父RDD中的信息将全部被移除。
  1. 检查点的触发时机/时间?
必须要action操作才能触发,但是血缘关系为了数据安全,会从血缘关系的最开始执行一遍。

7. 设置检查点步骤

  • 设置检查点数据存储路径:sc.setCheckpointDir("./checkpoint1")
  • 设置检查点方法:wordToOneRdd.checkpoint()
  1. 设置检查点案例

在开发环境,一般检查点和缓存配合使用

object Spark02_checkpoint {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交Spark App的入口
    val sc: SparkContext = new SparkContext(conf)

    //设置检查点目录
    sc.setCheckpointDir("D:\\dev\\workspace\\bigdata-0105\\spark-0105\\cp")

    //开发环境,应该将检查点目录设置在hdfs上
    // 设置访问HDFS集群的用户名
    //System.setProperty("HADOOP_USER_NAME","mayi")
    //sc.setCheckpointDir("hdfs://hadoop202:8020/cp")



    //创建RDD
    val rdd: RDD[String] = sc.makeRDD(List("hello bangzhang","hello jingjing"),2)

    //扁平映射
    val flatMapRDD: RDD[String] = rdd.flatMap(_.split(" "))

    //结构转换
    val mapRDD: RDD[(String, Long)] = flatMapRDD.map {
      word => {
        (word, System.currentTimeMillis())
      }
    }

    //打印血缘关系
    println(mapRDD.toDebugString)


    //在开发环境,一般检查点和缓存配合使用
    mapRDD.cache()

    //设置检查点
    mapRDD.checkpoint()

    //触发行动操作
    mapRDD.collect().foreach(println)

    println("---------------------------------------")

    //打印血缘关系
    println(mapRDD.toDebugString)

    //释放缓存
    //mapRDD.unpersist()

    //触发行动操作
    mapRDD.collect().foreach(println)

    // 关闭连接
    sc.stop()
  }
}

3. 缓存和检查点的区别?

  • 1)Cache缓存只是将数据保存起来,不切断血缘依赖。Checkpoint检查点切断血缘依赖。
  • 2)Cache缓存的数据通常存储在磁盘、内存等地方,可靠性低。Checkpoint的数据通常存储在HDFS等容错、高可用的文件系统,可靠性高。
  • 3)建议对checkpoint()的RDD使用Cache缓存,这样checkpoint的job只需从Cache缓存中读取数据即可,否则需要再从头计算一次RDD。
  • 4)如果使用完了缓存,可以通过unpersist()方法释放缓存