RDD的持久化是Spark中的一个非常重要的概念。当我们持久化一个RDD时,每个节点将其所计算的每个分区持久化到内存中,这样用到这些数据的其他的行动算子就可以复用这一部分数据。(通常会比不持久化的速度快10倍)
RDD有两种持久化方式
checkpoint(快照),是spark中提供的另一個持久化的重要功能。因为有时候我们的Spark应用比较复杂,从初始化RDD开始,有很长一串的转换处理,且整个应用运行的时间可能也非常长。对于这类复杂的Spark任务,有很高的风险会出现某个要反复使用的RDD因为节点的故障导致数据丢失,如果失败的时候没有容错机制,当后面的Transformations算子又需要用到这个RDD时发现数据丢失,那么有需要从头到尾重新计算一次,直到该RDD算出结果。所以针对这类任务,如果我们担心某些关键的,后续反复使用的RDD因节点故障导致数据丢失,就可以针对该RDD做一个checkpoint,实现容错和高可用。
import org.apache.spark.{SparkConf, SparkContext}
object Persistence {
def main(args: Array[String]): Unit = {
val conf:SparkConf = new SparkConf()
conf.setMaster("local").setAppName("persistenceTest")
//2.创建sc
val sc:SparkContext = new SparkContext(conf)
//3.创建RDD
val inputPath:String = "D:\\javaworkspace\\BigData\\Spark\\SparkApp\\src\\main\\resources\\sensor.txt"
val rdd2 = sc.textFile(inputPath)
.filter(_.nonEmpty)
.map(data=>{
val arr = data.split(",")
SensorReading(arr(0),arr(1).toLong,arr(2).toDouble)
})
rdd2.cache()
sc.setCheckpointDir("./checkpoint")
rdd2.checkpoint()
rdd2.foreach(print)
sc.stop()
}
}
Tips:checkpoint()也只懒执行的,需要Action算子触发。
sparkContext设置checkpoint目录,用于存放checkpoint的数据,对RDD调用checkpoint时,该RDD就会被RDDCheckpointData对象进行管理,此时RDD的checkpoint状态会被设置为Initialized.
待RDD所在的job运行结束后,会调用job中最后一个RDD的doCheckpoint方法,该方法沿着RDD的依赖关系向上查找被checkpoint标记过的RDD,并将其checkpoint状态设置为CheckpointingInProgress
启动一个单独的job,来将有依赖关系中被标记为CheckpointingInProgress的RDD执行checkpoint操作,将数据序列化后写入到checkpoint目录中。
将RDD数据写入到checkpoint目录后,会将RDD状态变为Checkpointed,并且还会改变RDD的依赖关系,会清除掉其所有依赖的RDD,并设置其父RDD为保存的checkpointRDD。
一般checkpoint都是和cache或者persist结合使用的,为什么要这样使用呢?
答:我们知道RDD是不存储数据的,由上面的流程我们知道,在Action算子触发执行完毕后启动单独的job来执行将RDD的数据写入到checkpoint目录中,这时RDD的计算已经完成,RDD中没有存储数据,就需要重新从头开始计算得出RDD的数据,再写入到checkpoint目录中,效率会比较低,所以推荐和cache或者persist一起使用,这样就不需要重新计算RDD的数据。
cache()将RDD持久化到内存中,查看spark源码,cache就是由persist()实现的。
import org.apache.spark.storage.StorageLevel
import org.apache.spark.{SparkConf, SparkContext}
object Persistence {
def main(args: Array[String]): Unit = {
val conf:SparkConf = new SparkConf()
conf.setMaster("local").setAppName("persistenceTest")
//2.创建sc
val sc:SparkContext = new SparkContext(conf)
//3.创建RDD
val inputPath:String = "D:\\javaworkspace\\BigData\\Spark\\SparkApp\\src\\main\\resources\\sensor.txt"
val rdd2 = sc.textFile(inputPath)
.filter(_.nonEmpty)
.map(data=>{
val arr = data.split(",")
SensorReading(arr(0),arr(1).toLong,arr(2).toDouble)
})
val rdd3 = rdd2.map(data=>{data})
//rdd3.cache()
rdd3.persist(StorageLevel.MEMORY_ONLY)
val start:Long = System.currentTimeMillis()
rdd3.count()
val end:Long = System.currentTimeMillis()
println("==========================firtsttime:"+(end-start))
rdd3.count()
val start2:Long = System.currentTimeMillis()
println("==========================secondtime:"+(start2-end))
sc.stop()
}
}
@DeveloperApi
class StorageLevel private(
private var _useDisk: Boolean,
private var _useMemory: Boolean,
private var _useOffHeap: Boolean,
private var _deserialized: Boolean,
private var _replication: Int = 1)
extends Externalizable
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 DISK_ONLY_3 = new StorageLevel(true, false, false, false, 3)
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)
持久化级别 | 说明 |
---|---|
MEMORY_ONLY | 仅存在内存中 |
MEMORY_AND_DISK | 存储在内存和磁盘中 |
MEMORY_ONLY_SER | 存储在内存中并序列化 |
MEMORY_AND_DISK_SER | 存储在内存中和磁盘中并序列化 |
DISK_ONLY | 仅存储在磁盘中 |
MEMORY_ONLY_2 | 存储在内存中,并设置1个副本 |
MEMORY_AND_DISK_2 | 存储在内存和磁盘中,并设置1个副本 |
OFF_HEAP | 存储在磁盘中,内存中并使用堆内存 |