Spark 运行时对哪些数据会做缓存?

在Spark应用执行过程中,某些需要多次使用或者重新计算的数据会进行缓存,为后续更多的计算操作复用,避免了再次计算,从而减少应用的执行时间,加速整体计算进度。

那么,在计算过程中,哪些数据是需要被缓存的。接下来我们通过一个简单的例子来回答一下。

如下示例,首先对输入数据进行map()计算,得到 mappedRDD,然后再对 mappedRDD 分别进行两种计算:reduceByKey + foreach(println) 和 groupByKey + foreach(println)。

java 复制代码
//输入数据
var inputRDD = sc.parallelize(Array[(Int,String)])(
(1,"a"),(2,"b"),(3,"c"),(4,"d"),(5,"e"),(3,"f"),(2,"g"),(1,"h"),(2,"i"),
),3)
val mappedRDD = inputRDD.map(r => (r._1 + 1, r._2))
// mappedRDD.cache()
val reducedRDD = mappedRDD.reduceByKey((x,y) => x + "_" + y, 2)
reducedRDD.foreach(println)
val groupedRDD = mappedRDD.groupByKey(3).mapValues(V => V.toList)
groupedRDD.foreach(println)

由于应用中存在两个foreach()行动算子,那么就会形成两个job,并且这两个job都是从inputRDD开始计算的。如下图所示。

不难看出,在这两个job中,inputRDD => mappedRDD 的计算流程都是一样的,理论上第二个job可以直接从 mappedRDD 开始进行计算的。

我们在代码中取掉mappedRDD.cache()语句的注释,声明一下mappedRDD需要被缓存。

需要注意的是:cache() 操作表示将数据直接写入到内存中,并且cache() 是lazy操作,并不会立即执行的,只有在第一个job运行时才将要缓存的数据写入内存中。

java 复制代码
//输入数据
var inputRDD = sc.parallelize(Array[(Int,String)])(
(1,"a"),(2,"b"),(3,"c"),(4,"d"),(5,"e"),(3,"f"),(2,"g"),(1,"h"),(2,"i"),
),3)
val mappedRDD = inputRDD.map(r => (r._1 + 1, r._2))
mappedRDD.cache()
val reducedRDD = mappedRDD.reduceByKey((x,y) => x + "_" + y, 2)
reducedRDD.foreach(println)
val groupedRDD = mappedRDD.groupByKey(3).mapValues(V => V.toList)
groupedRDD.foreach(println)

如下图所示,对 mappedRDD 进行缓存之后,可以避免第二个job再重复map() 计算。

但是带来的代价就是会只占用很多内存空间来缓存数据,试想,如果mappedRDD 包含了上亿个record,那么其存储将带来很大的内存消耗。这时设置缓存并非最优解,需要权衡计算成本和存储成本。

在本例中,map()操作的计算逻辑很简单,只需要少量的计算成本,如果mappedRDD需要很大的存储空间时,那么就不会对其进行缓存。

缓存机制实际上是一种用空间来换时间的方式,那么如何判断数据是否应该被设置缓存呢?

在 spark 缓存机制中,只要满足如下三个条即可:

(1)会被重复使用的数据 。并且被重复使用次数越多,那么缓存带来的性价比也会越高。一般来说,迭代型和交互型的应用会比较适合。

(2)数据不适合过大 。因为数据量级过大,会占用大量的存储空间,导致内存不足,会直接降低数据计算可使用的空间(可参考上一篇spark内存的介绍)。虽然说缓存过大也可以存放在磁盘中,但是磁盘IO的成本也比较高,甚至不如重新计算的成本低。

(3)非重复缓存的数据。什么意思呢?其实就是如果缓存了某RDD,那么与其存在一对一血缘的父RDD就不需要缓存了。在本例中,如果对 mappedRDD 进行了缓存,那么就没有必要对 inputRDD 进行缓存了。除非有新的job会重复用到inputRDD且不会用到 mappedRDD。

除此之外,不仅RDD 可以被缓存,广播数据task计算结果数据也可以被缓存。

相关推荐
艾妮艾妮13 分钟前
C语言常见3种排序
java·c语言·开发语言·c++·算法·c#·排序算法
java技术小馆14 分钟前
Zookeeper中的Zxid是如何设计的
java·分布式·zookeeper·云原生
葵野寺24 分钟前
【多线程】synchronized锁升级和优化
java·开发语言·java-ee
SimonKing33 分钟前
因为不知道条件注解@Conditional,错失15K的Offer!
java·后端·架构
橘猫云计算机设计34 分钟前
基于springboot微信小程序的旅游攻略系统(源码+lw+部署文档+讲解),源码可白嫖!
java·spring boot·后端·微信小程序·毕业设计·旅游
落榜程序员35 分钟前
Java 基础-30-单例设计模式:懒汉式与饿汉式
java·开发语言
顾林海36 分钟前
深度解析ArrayList工作原理
android·java·面试
雷渊37 分钟前
spring-IoC容器启动流程源码分析
java·后端·面试
用户33154891110742 分钟前
一招搞定Java线程池炸弹,系统吞吐量暴增10倍!
java·后端
努力的搬砖人.1 小时前
maven如何使用
java·后端·面试·maven