SPARK-RDD持久化

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/awiner/article/details/84982067

RDD持久化

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操作时,进行数据的持久化,比如写入磁盘,主要是为了在节点失败时,避免需要重新计算整个过程。



RDD持久化代码编写

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;

/**
 * RDD持久化 
 */

public class Persist{
    public void main(String[] args){
    SparkConf conf = new SparkConf().setAppName("Persist").setMaster("local");
    
    JavaSparkContext sc = new JavaSparkContext(conf);
    
    //cache()和persist()的使用规则
    //必须在transformation或者textFile等创建一个RDD之后,直接连续调用cache()或者persist方法
    //不可单起一行创建
    
    
    JavaRDD<String> lines =sc.textFile("D://spark.txt").cache(); //
    //JavaRDD<String> lines =sc.textFile("D://spark.txt")         
    long beginTime = System.currentTimeMillis();
    
    long count =lines.count();
    System.out.println(count);
    
    long endTime= System.currentTimeMillis();
    
    System.out.println(endTime-beginTime);//花费时长


    beginTime = System.currentTimeMillis();
    count = lines.count();
    System.out.println(count);  
    endTime = System.currentTimeMillis();
    System.out.println( endTime - beginTime + " 毫秒");

    sc.close
        
    }
    //希望找个大一点文件去提测试,小文件很容易结果不同。
}









RDD持久化cache()跟persist()的区别

cache()只有一个默认的缓存级别<MEMORY_ONLY>,是persist的简化方式

persist()可以根据情况设置其它的缓存级别

//源码来自spark-2.10版本
 /**
  * Persist this RDD with the default storage level (`MEMORY_ONLY`).
  */
 def cache(): JavaRDD[T] = wrapRDD(rdd.cache())

 /**
  * Set this RDD's storage level to persist its values across operations after the first time
  * it is computed. This can only be used to assign a new storage level if the RDD does not
  * have a storage level set yet..
  */
 def persist(newLevel: StorageLevel): JavaRDD[T] = wrapRDD(rdd.persist(newLevel))

 /**
  * Mark the RDD as non-persistent, and remove all blocks for it from memory and disk.
  * This method blocks until all blocks are deleted.
  */
 def unpersist(): JavaRDD[T] = wrapRDD(rdd.unpersist())

 /**
  * Mark the RDD as non-persistent, and remove all blocks for it from memory and disk.
  *
  * @param blocking Whether to block until all blocks are deleted.
  */
 def unpersist(blocking: Boolean): JavaRDD[T] = wrapRDD(rdd.unpersist(blocking))

RDD缓存级别StorageLevel

MEMORY_ONLY
   以非序列化的java对象的方式持久化在JVM内存中。如果内存无法完全存储RDD所有的partition(分区),那些没有持久化的partition就会在下一次需要使用它的时候,重新被计算

MEMORY_AND_DISK
   基本解释同上,但是某些partition无法存储到内存时,会持久滑到磁盘中。下次需要这些partition时,需从磁盘上读取数据。
MEMORY_ONLY_SER
   同MEMORY_ONLY,但是会使用Java序列化方式,将Java对象序列化后进行持久化。可以减少内存开销,但是需要进行反序列化,因此会加大CPU开销。
MEMORY_AND_DSK_SER
   同MEMORY_AND_DSK。但是使用序列化方式持久化Java对象。

DISK_ONLY
   使用非序列化Java对象的方式持久化,完全存储到磁盘上。
MEMORY_ONLY_2
MEMORY_AND_DISK_2
   如果是尾部加了2的持久化级别,表示会将持久化数据复用一份,保存到其他节点,从而在数据丢失时,不需要再次计算,只需要使用备份数据即可
   

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")
 }

如何选择RDD持久化策略

Spark提供的多种持久化级别,主要是为了在CPU和内存消耗之间进行取舍。下面是一些通用的持久化级别的选择建议:

1、优先使用MEMORY_ONLY,如果可以缓存所有数据的话,那么就使用这种策略。因为纯内存速度最快,而且没有序列化,不需要消耗CPU进行反序列化操作。
2、如果MEMORY_ONLY策略,无法存储的下所有数据的话,那么使用MEMORY_ONLY_SER,将数据进行序列化进行存储,纯内存操作还是非常快,只是要消耗CPU进行反序列化。
3、如果需要进行快速的失败恢复,那么就选择带后缀为_2的策略,进行数据的备份,这样在失败时,就不需要重新计算了。
4、能不使用DISK相关的策略,就不用使用,有的时候,从磁盘读取数据,还不如重新计算一次。

参考很多博客,感谢!

猜你喜欢

转载自blog.csdn.net/awiner/article/details/84982067