Scala (为什么)我们需要在RDD上调用缓存还是持久化

Scala (为什么)我们需要在RDD上调用缓存还是持久化,scala,apache-spark,rdd,Scala,Apache Spark,Rdd,当从文本文件或集合(或从另一个RDD)创建弹性分布式数据集(RDD)时,我们是否需要显式调用“缓存”或“持久化”来将RDD数据存储到内存中?或者RDD数据默认以分布式方式存储在内存中 val textFile = sc.textFile("/user/emp.txt") 根据我的理解,在上述步骤之后,textFile是一个RDD,可以在节点的所有/部分内存中使用 如果是这样,为什么我们需要在文本文件RDD上调用“缓存”或“持久化”?我们需要显式调用“缓存”或“持久化”来将RDD数据存储到内存中

当从文本文件或集合(或从另一个RDD)创建弹性分布式数据集(RDD)时,我们是否需要显式调用“缓存”或“持久化”来将RDD数据存储到内存中?或者RDD数据默认以分布式方式存储在内存中

val textFile = sc.textFile("/user/emp.txt")
根据我的理解,在上述步骤之后,textFile是一个RDD,可以在节点的所有/部分内存中使用

如果是这样,为什么我们需要在文本文件RDD上调用“缓存”或“持久化”?

我们需要显式调用“缓存”或“持久化”来将RDD数据存储到内存中吗?

是的,只有在需要的时候

默认情况下以分布式方式存储在内存中的RDD数据?

原因如下:

  • Spark支持两种类型的共享变量:广播变量(可用于在所有节点上的内存中缓存值)和累加器(仅“添加”到其中的变量,如计数器和总和)

  • RDD支持两种类型的操作:转换(从现有数据集创建新数据集)和操作(在数据集上运行计算后向驱动程序返回值)。例如,map是一种转换,它通过一个函数传递每个数据集元素,并返回一个表示结果的新RDD。另一方面,reduce是一个操作,它使用某个函数聚合RDD的所有元素,并将最终结果返回给驱动程序(尽管还有一个并行reduceByKey返回分布式数据集)

  • Spark中的所有转换都是惰性的,因为它们不会立即计算结果。相反,他们只记得应用于某些基本数据集(例如文件)的转换。仅当操作需要将结果返回到驱动程序时,才会计算转换。这种设计使Spark能够更高效地运行–例如,我们可以意识到,通过map创建的数据集将用于reduce,并且只将reduce的结果返回给驱动程序,而不是更大的映射数据集

  • 默认情况下,每次对每个转换的RDD运行操作时,都可以重新计算它但是,您也可以使用persist(或cache)方法在内存中持久化RDD,在这种情况下,Spark将保留集群中的元素,以便下次查询时更快地访问它。还支持在磁盘上持久化RDD,或跨多个节点复制RDD


有关更多详细信息,请查看。

大多数RDD操作都是惰性的。将RDD视为一系列操作的描述。RDD不是数据。所以这一行:

val textFile = sc.textFile("/user/emp.txt")
它什么也不做。它创建了一个RDD,上面写着“我们需要加载这个文件”。此时未加载该文件

需要观察数据内容的RDD操作不能是惰性的。(这些称为操作。)例如
RDD.count
-要告诉您文件中的行数,需要读取文件。因此,如果您写入
textFile.count
,此时将读取文件,对行进行计数,并返回计数

如果您再次调用
textFile.count
怎么办?同样的事情:文件将被再次读取和计数。没有存储任何内容。RDD不是数据

那么,
RDD.cache
做什么呢?如果将
textFile.cache
添加到上述代码中:

val textFile = sc.textFile("/user/emp.txt")
textFile.cache
它什么也不做
RDD.cache
也是一种惰性操作。文件仍未读取。但是现在RDD说“读取这个文件,然后缓存内容”。如果随后第一次运行
textFile.count
,将加载、缓存和计数文件。如果再次调用
textFile.count
,操作将使用缓存。它只需从缓存中获取数据并计算行数


缓存行为取决于可用内存。例如,如果该文件不适合内存,则
textFile.count
将返回到通常的行为并重新读取该文件。

我认为这个问题最好表述为:

什么时候需要在RDD上调用缓存或持久化? Spark进程是懒惰的,也就是说,在需要它之前什么都不会发生。 为了快速回答这个问题,在发出
val textFile=sc.textFile(“/user/emp.txt”)
之后,数据不会发生任何变化,只构建了一个
HadoopRDD
,使用该文件作为源

假设我们稍微变换一下数据:

val wordsRDD = textFile.flatMap(line => line.split("\\W"))
同样,数据没有发生任何变化。现在有了一个新的RDD
wordsRDD
,它包含了对
testFile
的引用和一个在需要时应用的函数

只有在对RDD调用操作时,如
wordsRDD.count
,才会执行名为沿袭的RDD链。也就是说,分解为分区的数据将由Spark cluster的执行器加载,将应用
flatMap
函数并计算结果

在线性沿袭上,如本例中的沿袭,不需要
cache()
。数据将被加载到执行器,所有转换将被应用,最后将计算
计数
,全部在内存中-如果数据适合内存中

缓存
在RDD分支时非常有用。假设您想将上一个示例中的单词过滤为肯定和否定单词的计数。你可以这样做:

val positiveWordsCount = wordsRDD.filter(word => isPositive(word)).count()
val negativeWordsCount = wordsRDD.filter(word => isNegative(word)).count()
在这里,每个分支都会重新加载数据。添加显式的
cache
语句将确保保留和重用以前完成的处理。工作将如下所示:

val textFile = sc.textFile("/user/emp.txt")
val wordsRDD = textFile.flatMap(line => line.split("\\W"))
wordsRDD.cache()
val positiveWordsCount = wordsRDD.filter(word => isPositive(word)).count()
val negativeWordsCount = wordsRDD.filter(word => isNegative(word)).count()
因此,
cache
被称为“破坏沿袭”,因为它创建了一个检查点,可以重新用于进一步处理

经验法则:当您的RDD沿袭时,请使用
缓存
。br