RDD Persistence
Spark非常重要的一个功能特性就是可以将RDD持久化在内存中。当对RDD执行持久化操作时,每个节点都会将自己操作的RDD的partition持久化到内存中,并且在之后对该RDD的反复使用中,直接使用内存缓存的partition。这样的话,对于针对一个RDD反复执行多个操作的场景,就只要对RDD计算一次即可,后面直接使用该RDD,而不需要反复计算多次该RDD。
巧妙使用RDD持久化,甚至在某些场景下,可以将spark应用程序的性能提升10倍。对于迭代式算法和快速交互式应用来说,RDD持久化,是非常重要的。
要持久化一个RDD,只要调用其cache()或者persist()方法即可。在该RDD第一次被计算出来时,就会直接缓存在每个节点中。而且Spark的持久化机制还是自动容错的,如果持久化的RDD的任何partition丢失了,那么Spark会自动通过其源RDD,使用transformation操作重新计算该partition。
cache()和persist()的区别在于,cache()是persist()的一种简化方式,cache()的底层就是调用的persist()的无参版本,同时就是调用persist(MEMORY_ONLY),将数据持久化到内存中。如果需要从内存中清楚缓存,那么可以使用unpersist()方法。
Spark自己也会在shuffle操作时,进行数据的持久化,比如写入磁盘,主要是为了在节点失败时,避免需要重新计算整个过程。
Spark RDD持久化使用方式如下:
- RDD.persist(存储等级) —— 可以指定存储等级,默认是MEMORY_ONLY。
- RDD.unpersist() —— 取消持久化
默认情况下,spark使用最近最少使用的算法(LRU)来删除旧的和未使用的RDD,以释放更多的内存。
持久化级别
StorageLevel | 说明 |
---|---|
MEMORY_ONLY | 默认选项。使用未序列化的Java对象格式,将数据保存在内存中。如过RDD太大无法完全存储在内存,多余的RDD partitions不会cache在内存,而是需要是在重新计算 |
MEMORY_AND_DISK | 使用未序列化的Java对象格式,优先尝试将数据保存在内存中。如果内存不够存放所有的数据,会将数据写入磁盘文件中。不会立刻输出到磁盘 |
MEMORY_ONLY_SER | 与MEMORY_ONLY类似。RDD的每个partition会被序列化成一个字节数组,节省空间,但需要反序列化才能使用,所以会使用更多CPU资源 |
MEMORY_AND_DISK_SER | 序列化存储,超出部分写入磁盘文件中 |
DISK_ONLY | 使用未序列化的Java对象格式,将数据全部写入磁盘文件中 |
注:对于上述任意一种持久化策略,如果加上后缀_2,代表把持久化数据存为两份。
持久化策略的选择
Spark提供的多种持久化级别,主要是为了在CPU和内存消耗之间进行取舍。下面是一些通用的持久化级别的选择建议:
1、优先使用MEMORY_ONLY,如果可以缓存所有数据的话,那么就使用这种策略。因为纯内存速度最快,而且没有序列化,不需要消耗CPU进行反序列化操作。
2、如果MEMORY_ONLY策略,无法存储的下所有数据的话,那么使用MEMORY_ONLY_SER,将数据进行序列化进行存储,纯内存操作还是非常快,只是要消耗CPU进行反序列化。
3、如果需要进行快速的失败恢复,那么就选择带后缀为_2的策略,进行数据的备份,这样在失败时,就不需要重新计算了。
4、能不使用DISK相关的策略,就不用使用,有的时候,从磁盘读取数据,还不如重新计算一次。