目录
Spark持久化策略_缓存优化
RDD的持久化策略
当某个RDD需要进行频繁复用的时候,spark提供RDD的持久化功能,可以通过使用persist()、cache()两种方法进行RDD的持久化。如下所示:
//scala
myRDD.persist()
myRDD.cache()
为什么要使用持久化?
因为RDD1经过Action生成新的RDD2之后,原先的RDD1就会被从内存中删除,如果在接下来的操作中还需要复用到RDD1,Spark会一路向上追溯,重新读取数据,然后重新计算出RDD1,然后进行计算。这会增加磁盘IO和计算成本,持久化会保存数据,等下一次Action时直接使用。
cache和persist的源码
def persist(newLevel: StorageLevel): this.type = {
if (isLocallyCheckpointed) {
// This means the user previously called localCheckpoint(), which should have already
// marked this RDD for persisting. Here we should override the old storage level with
// one that is explicitly requested by the user (after adapting it to use disk).
persist(LocalRDDCheckpointData.transformStorageLevel(newLevel), allowOverride = true)
} else {
persist(newLevel, allowOverride = false)
}
}
/**
* Persist this RDD with the default storage level (`MEMORY_ONLY`).
*/
def persist(): this.type = persist(StorageLevel.MEMORY_ONLY)
/**
* Persist this RDD with the default storage level (`MEMORY_ONLY`).
*/
def cache(): this.type = persist()
从源码中我们可以看到, cache方法实际上是调用无参数传递的persis方法,所以我们只要研究persist方法即可。而无参的persist默认的参数是StorageLevel.MEMORY_ONLY,我们可以看一下类StorageLevel的源码。
/**
* Various [[org.apache.spark.storage.StorageLevel]] defined and utility functions for creating
* new storage levels.
*/
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)
/**
* :: DeveloperApi ::
* Return the StorageLevel object with the specified name.
*/
@DeveloperApi
def fromString(s: String): StorageLevel = s match {
case "NONE" => NONE
case "DISK_ONLY" => DISK_ONLY
case "DISK_ONLY_2" => DISK_ONLY_2
case "MEMORY_ONLY" => MEMORY_ONLY
case "MEMORY_ONLY_2" => MEMORY_ONLY_2
case "MEMORY_ONLY_SER" => MEMORY_ONLY_SER
case "MEMORY_ONLY_SER_2" => MEMORY_ONLY_SER_2
case "MEMORY_AND_DISK" => MEMORY_AND_DISK
case "MEMORY_AND_DISK_2" => MEMORY_AND_DISK_2
case "MEMORY_AND_DISK_SER" => MEMORY_AND_DISK_SER
case "MEMORY_AND_DISK_SER_2" => MEMORY_AND_DISK_SER_2
case "OFF_HEAP" => OFF_HEAP
case _ => throw new IllegalArgumentException(s"Invalid StorageLevel: $s")
}
/**
* :: DeveloperApi ::
* Create a new StorageLevel object.
*/
@DeveloperApi
def apply(
useDisk: Boolean,
useMemory: Boolean,
useOffHeap: Boolean,
deserialized: Boolean,
replication: Int): StorageLevel = {
getCachedStorageLevel(
new StorageLevel(useDisk, useMemory, useOffHeap, deserialized, replication))
}
/**
* :: DeveloperApi ::
* Create a new StorageLevel object without setting useOffHeap.
*/
@DeveloperApi
def apply(
useDisk: Boolean,
useMemory: Boolean,
deserialized: Boolean,
replication: Int = 1): StorageLevel = {
getCachedStorageLevel(new StorageLevel(useDisk, useMemory, false, deserialized, replication))
}
可以看到,StorageLevel参数包括:
参数:默认 | 含义 |
useDisk: Boolean | 是否使用磁盘做持久化 |
useMemory: Boolean | 是否使用内存做持久化 |
useOffHeap: Boolean | 是否使用JAVA堆内存 |
deserialized: Boolean | 是否序列化 |
replication:1 | 副本数(做容错) |
所以我们可以得到:
- NONE:是默认的配置
- DISK_ONLY:仅仅缓存于磁盘
- DISK_ONLY_2:仅仅缓存于磁盘并且保持2个副本
- MEMORY_ONLY:仅仅缓存于磁盘内存
- MEMORY_ONLY_2:仅仅缓存于磁盘内存并且保持2个副本
- MEMORY_ONLY_SER:仅仅缓存于磁盘内存且序列化
- MEMORY_ONLY_SER_2:仅仅缓存于磁盘内存且序列化和保持2个副本
- MEMORY_AND_DISK:缓存于内存满之后,就会缓存于磁盘
- MEMORY_AND_DISK_2:缓存于内存满之后,就会缓存于磁盘并且保持2个副本
- MEMORY_AND_DISK_SER:缓存于内存满之后,就会缓存于磁盘且序列化
- MEMORY_AND_DISK_SER_2:缓存于内存满之后,就会缓存于磁盘且序列化,以及保持2个副本
- OFF_HEAP:缓存远离堆内存
序列化可以类似于压缩,便于节省存储空间,但会增加计算成本,因为每次使用需要序列化以及反序列化;副本数量默认为1,是为了防止数据丢失,增强容错能力;OFF_HEAP将RDD存储在etachyon上,使得具有更低的垃圾回收开销,了解即可;DISK_ONLY没什么可说的,下面主要比较MEMORY_ONLY和MEMORY_AND_DISK。
MEMORY_ONLY和MEMORY_AND_DISK
MEMORY_ONLY:RDD仅缓存与内存中,内存中放不下的分区将在被使用时重新从磁盘读取数据并计算。
MEMORY_AND_DISK:尽量往内存中存,存不下的分区将会被保存在磁盘中,避免了重算的过程。
直观看来,MEMORY_ONLY还需要计算过程,效率相对来说较低,但事实上,由于是在内存中进行计算,所以重新计算时间消耗是远小于磁盘IO的,所以通常默认使用MEMORY_ONLY。除非中间计算开销特别大,这时候使用MEMORY_AND_DISK才会是一个更好的选择。
总结