我正在努力了解Spark的缓存是如何工作的。
这是我的天真理解,如果我遗失了某些内容,请告诉我:
val rdd1 = sc.textFile("some data")
rdd1.cache() //marks rdd1 as cached
val rdd2 = rdd1.filter(...)
val rdd3 = rdd1.map(...)
rdd2.saveAsTextFile("...")
rdd3.saveAsTextFile("...")
在上面,rdd1将仅从磁盘(例如HDFS)加载一次。 (当我保留rdd2时),然后从缓存中(假设有足够的RAM)保存rdd3时)
现在这是我的问题。假设我想缓存rdd2和rdd3,因为它们将在以后使用,但在创建后我不需要rdd1。
基本上有重复,不是吗?因为一旦计算出rdd2和rdd3,我就不再需要rdd1了,我应该可以解决它,对吧?问题是什么时候?
这会有效吗? (选项A)
val rdd1 = sc.textFile("some data")
rdd1.cache() // marks rdd as cached
val rdd2 = rdd1.filter(...)
val rdd3 = rdd1.map(...)
rdd2.cache()
rdd3.cache()
rdd1.unpersist()
spark是否会向DAG添加unpersist调用?还是立即完成?如果它立即完成,那么当我从rdd2和rdd3读取时,基本上rdd1将被非缓存,对吗?
我应该这样做(选项B)吗?
val rdd1 = sc.textFile("some data")
rdd1.cache() // marks rdd as cached
val rdd2 = rdd1.filter(...)
val rdd3 = rdd1.map(...)
rdd2.cache()
rdd3.cache()
rdd2.saveAsTextFile("...")
rdd3.saveAsTextFile("...")
rdd1.unpersist()
所以问题是:
选项A是否足够好?即rdd1
只会加载文件一次吗?
或者我需要选择B吗?
答案 0 :(得分:23)
似乎需要选项B.原因与Spark如何执行persist / cache和unpersist有关。由于RDD转换仅构建DAG描述而不执行,因此在您调用unpersist时,在选项A中,您仍然只有作业描述而不是正在运行的执行。
这是相关的,因为cache
或persist
调用只是将RDD添加到RDD的Map中,这些RDD标记为在作业执行期间保持持久性。但是,unpersist
直接告诉blockManager从存储中驱逐RDD并删除持久RDD映射中的引用。
所以你需要在Spark实际执行后调用unpersist并将RDD与块管理器一起存储。
RDD.persist
方法的评论提示:
rdd.persist
答案 1 :(得分:2)
在选项A中,您在调用操作时没有显示(调用保存)
val rdd1 = sc.textFile("some data")
rdd.cache() //marks rdd as cached
val rdd2 = rdd1.filter(...)
val rdd3 = rdd1.map(...)
rdd2.cache()
rdd3.cache()
rdd1.unpersist()
rdd2.saveAsTextFile("...")
rdd3.saveAsTextFile("...")
如果序列如上,则选项A应使用缓存版本的rdd1来计算rdd2和rdd 3
答案 2 :(得分:0)
选项B是一种小调整的最佳方法。利用较便宜的行动方法。在您的代码提到的方法中,saveAsTextFile是一个昂贵的操作,用count方法替换它。
这里的想法是从DAG中移除大rdd1,如果它与进一步计算无关(在创建rdd2和rdd3之后)
从代码
更新了方法val rdd1 = sc.textFile("some data").cache()
val rdd2 = rdd1.filter(...).cache()
val rdd3 = rdd1.map(...).cache()
rdd2.count
rdd3.count
rdd1.unpersist()