Spark每日半小时(5)——持久化(缓存)

如前所述,SparkRDD是惰性求值的,而有时我们希望能多次使用同一个RDD。如果简单地对RDD调用行动操作,Spark每次都会重算RDD以及它的所有依赖。这在迭代算法中消耗格外大,因为迭代算法常常会多次使用同一组数据。下例就是先对RDD作一次计数、再把该RDD输出的一个小例子。

        SparkConf conf = new SparkConf().setMaster("local").setAppName("simpleDemo");

        JavaSparkContext sc = new JavaSparkContext(conf);

        JavaRDD lines = sc.textFile("README.md");
        System.out.println(lines.count());
        System.out.println(StringUtils.join(lines.collect(), ","));

为了避免多次计算同一个RDD,可以让Spark对数据进行持久化。当我们让Spark持久化存储一个RDD时,计算出RDD的节点会分别保存他们所求出的分区数据。如果一个有持久化数据的节点发生故障,Spark会在需要用到缓存的数据时重算丢失的数据分区。如果希望节点故障的情况不会拖累我们的执行速度,也可以把数据备份到多个节点上。

出于不同的目的,我们可以为RDD选择不同的持久化级别。Java中,默认情况下persist()会把数据以序列化的形式缓存在JVM的堆空间中。当我们把数据写到磁盘或者堆外存储上时,也总是使用序列化后的数据。

StorageLevel中的持久化级别,通过咋存储级别末尾上加"_2"来把持久化数据存为两份
级别 使用的空间 CPU时间 是否在内存中 是否在磁盘上 备注
MEMORY_ONLY  
MEMORY_ONLY_SER  
MEMORY_AND_DISK 中等 部分 部分 如果数据在内存中放不下,则溢写到磁盘上
MEMORY_AND_DISK_SER 部分 部分 如果数据在内存中放不下,则溢写到磁盘上,在内存中存放序列化后的数据
DISK_ONLY  

下面来看下持久化在Java中的用法

        SparkConf conf = new SparkConf().setMaster("local").setAppName("simpleDemo");

        JavaSparkContext sc = new JavaSparkContext(conf);

        JavaRDD lines = sc.textFile("README.md");

        lines.persist(StorageLevels.MEMORY_AND_DISK_SER_2);

        System.out.println(lines.count());
        System.out.println(StringUtils.join(lines.collect(), ","));

我们在第一次对这个RDD调用行动操作前就调用了persist()方法。persist()调用本身不会触发强制求值。

如果要缓存的数据太多,内存中放不下,Spark会自动利用最近最少使用(LRU)的缓存策略把最老的分区从内存中移除。对于仅把数据存放在内存中的缓存级别,下一次要用到已经被移除的分区时,这些分区就需要重新计算。但是对于使用内存与磁盘的缓存级别的分区来说,被移除的分区都会写入磁盘。不论哪一种情况,都不必担心你的作业因为缓存了太多数据而被打断。不过,缓存不必要的数据会导致有用的数据被移出内存,带来更多重算的时间开销。

最后,RDD还有一个方法叫做unpersist(),调用该方法可以手动把持久化的RDD从缓存中移除。

你可能感兴趣的:(#,大数据——Spark每日半小时,#,Spark每日半小时)