persist和cache原理和使用
// org.apache.spark.rdd/rdd.scala
def cache(): this.type = persist()
def persist(): this.type = persist(StorageLevel.MEMORY_ONLY)
cache基于persist实现,本文重点对persist进行研究
1.persist原理
1.1 persist初衷
Spark基于内存进行计算,不会缓存中间结果,如果计算中多次引用同一个RDD,那么每一次对该RDD的引用都会在action触发计算时重新计算该RDD,会导致大量的重复计算,Spark提供了一种RDD的持久化机制来实现RDD重用,即用调用rdd.persist()方法标记rdd需要持久化,在下一次计算该rdd时会将rdd的计算结果进行持久化。
1.2 持久化级别
// org.apache.spark.storage/StorageLevel.scala
class StorageLevel private(
private var _useDisk: Boolean,
private var _useMemory: Boolean,
private var _useOffHeap: Boolean,
private var _deserialized: Boolean,
private var _replication: Int = 1)
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)
}
StorageLevel类的成员变量
- _useDisk:是否使用磁盘
- _useMemory:是否使用内存
- _useOffHeap:是否使用堆外内存
- _deserialized:是否进行序列化
- _replication:副本数
StorageLevel伴随对象的12个持久化级别对象
磁盘 | 内存 | 堆外内存 | 序列化 | 副本数 | |
---|---|---|---|---|---|
NONE | 0 | ||||
DISK_ONLY | √ | 1 | |||
DISK_ONLY_2 | √ | 2 | |||
MEMORY_ONLY | √ | 1 | |||
MEMORY_ONLY_2 | √ | 2 | |||
MEMORY_ONLY_SER | √ | √ | 1 | ||
MEMORY_ONLY_SER_2 | √ | √ | 2 | ||
MEMORY_AND_DISK | √ | √ | 1 | ||
MEMORY_AND_DISK_2 | √ | √ | 2 | ||
MEMORY_AND_DISK_SER | √ | √ | √ | 1 | |
MEMORY_AND_DISK_SER_2 | √ | √ | √ | 2 | |
OFF_HEAP | √ | √ | √ | 1 |
说明
- 同时使用memory和disk时,优先使用内存,内存不足使用磁盘,磁盘不足时从头重新计算
- OFF_HEAP只使用堆外内存,在内存有限时,可以减少频繁GC及不必要的内存消耗,提升程序性能,但是没有数据备份,数据丢失需要重新计算
- RDD的默认持久化级别是NONE,即不进行持久化
- persist默认持久化级别为MEMORY_ONLY
// org.apache.spark.rdd/rdd.scala
abstract class RDD[T: ClassTag](
@transient private var _sc: SparkContext,
@transient private var deps: Seq[Dependency[_]]
) extends Serializable with Logging {
private var storageLevel: StorageLevel = StorageLevel.NONE //RDD默认持久化级别
}
2.persist使用场景
- 某个步骤计算非常耗时,需要进行persist持久化
- 计算链条非常长,重新恢复要算很多步骤
- 需要checkpoint的RDD最好进行persist,checkpoint机制会在job执行完成之后根据DAG向前回溯,找到需要进行checkpoint的RDD,另起一个job来计算该RDD,将计算结果存储到HDFS,如果在job执行的过程中对该RDD进行了persist,那么进行checkpoint会非常快
- shuffle之前进行persist,Spark默认将数据持久化到磁盘,自动完成,无需干预
- shuffle之后为什么要persist,shuffle要进性网络传输,风险很大,数据丢失重来,恢复代价很大