spark程序优化之一:善用 persist 方法
场景
在编写spark程序代码的时候,如果涉及大数据运算的时候,一次计算可能得几十分钟甚至一个小时以上,更极端的情况则是,一个较大的对象被多次使用,导致重复计算了很多次。这种做法就会消耗资源,也会浪费我们的时间。那么,针对这种情况,我们有什么方法来避免吗?嗯,很显然是有的,那就是将这个多次计算的对象进行缓存,第一次缓存之后,下次就不使用就可以调用,节省重复计算的时间。当然,这个要分情况,对于计算时间和计算资源消耗较大的时候,这个情况较为符合。如果本身数据量小,计算简单,就没必要。因为保存到缓存的时间比重复计算的时间更长。今天我们就来介绍函数 persist方法
适用场景
- spark版本: 1.6以上(不管是版本1.6还是版本2以上,这个函数定义是一致的,只是内部实现存在差异)
- 使用spark DataFrame进行运算
- 一个对象被多次计算
函数说明
让我们来看看spark1.6里面的persist方法代码:
/**
* Persist this [[DataFrame]] with the default storage level (`MEMORY_AND_DISK`).
* @group basic
* @since 1.3.0
*/
def persist(): this.type = {
sqlContext.cacheManager.cacheQuery(this)
this
}
/**
* Persist this [[DataFrame]] with the default storage level (`MEMORY_AND_DISK`).
* @group basic
* @since 1.3.0
*/
def cache(): this.type = persist()
/**
* Persist this [[DataFrame]] with the given storage level.
* @param newLevel One of: `MEMORY_ONLY`, `MEMORY_AND_DISK`, `MEMORY_ONLY_SER`,
* `MEMORY_AND_DISK_SER`, `DISK_ONLY`, `MEMORY_ONLY_2`,
* `MEMORY_AND_DISK_2`, etc.
* @group basic
* @since 1.3.0
*/
def persist(newLevel: StorageLevel): this.type = {
sqlContext.cacheManager.cacheQuery(this, None, newLevel)
this
}
从上面的代码,可以看到,
- 调用
cache方法其实就是调用无参的persist。 persist()其实相当于persist(StorageLevel.MEMORY_AND_DISK)。
其中比较重要的就是StorageLevel对象。这个对象主要是指定我们缓存的级别。那么让我们看看StorageLevel的内部成员:
class StorageLevel private(
private var _useDisk: Boolean, //是否使用磁盘
private var _useMemory: Boolean,//是否使用内存
private var _useOffHeap: Boolean,//是否使用堆外内存
private var _deserialized: Boolean,//是否反序列化
private var _replication: Int = 1//备份因子,默认为1)
extends Externalizable
那有多少种缓存策略呢,让我们看一下下表:
| 级别 | 使用空间 | CPU时间 | 是否在内存中 | 是否在磁盘上 | 备注 |
|---|---|---|---|---|---|
| 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份 |
| DISK_ONLY | 低 | 高 | 否 | 是 | |
| DISK_ONLY_2 | 低 | 高 | 否 | 是 | 数据存2份 |
| NONE | |||||
| OFF_HEAP |
总结
- 一般情况下,建议使用
persist(StorageLevel.MEMORY_AND_DISK)来替代cache()函数,避免因为内存不足而导致出差; - 内存足够的时候,可以直接使用 级别
MEMORY_ONLY。因为是直接内存操作,速度很快; - 如果空间不足,可以考虑使用级别
MEMORY_ONLY_SER,会节省空间,只是多了一步序列化操作; - 如果第三步还不行,就考虑使用级别
MEMORY_AND_DISK,将部分数据存放到磁盘当中; - 除非真的没办法,不然不推荐使用级别
DISK_ONLY。存磁盘再读取,会导致性能急剧降低,这个操作可能比重新计算还慢; - 级别后面跟着
_2则是代表进行数据备份。如果不是追求作业的高可用行,不推荐使用。 - OFF_HEAP,是为了
使用JVM堆外内存。使用OFF_HEAP的优势在于,在内存有限时,可以减少频繁的GC及不必要的内存消耗,提升程序性能
浙公网安备 33010602011771号