Answers:
大多数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
则将恢复为通常的行为并重新读取文件。
perisist
并选择一个存储选项,该选项允许将缓存数据溢出到磁盘上。
我认为这个问题最好表述为:
Spark进程是惰性的,也就是说,直到需要时,什么都不会发生。为了快速回答这个问题,val textFile = sc.textFile("/user/emp.txt")
发出文件后,数据什么都没有发生,仅HadoopRDD
使用文件作为源构造了一个。
假设我们对数据进行了一些转换:
val wordsRDD = textFile.flatMap(line => line.split("\\W"))
同样,数据没有任何反应。现在有一个新的RDD wordsRDD
,其中包含对RDD 的引用testFile
以及在需要时要应用的功能。
仅当在RDD上调用操作时,例如 wordsRDD.count
RDD链)时,才会执行称为血统的动作。也就是说,按分区细分的数据将由Spark集群的执行程序加载,该flatMap
函数将被应用并计算结果。
在线性血统中,cache()
不需要像本例中那样。数据将被加载到执行器,所有转换将被应用,最后count
将被计算,全部在内存中-如果数据适合内存。
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
据说它“破坏了血统”,因为它创建了可重新用于进一步处理的检查点。
经验法则:cache
当RDD的谱系分支出来或像循环中一样多次使用RDD时使用。
spark.storage.memoryFraction
。关于哪个执行程序具有哪些数据,RDD将跟踪其在执行程序上分布的分区。
cache
还是persist
可以打破沿袭。
我们是否需要显式调用“缓存”或“持久”将RDD数据存储到内存中?
是的,仅在需要时。
默认情况下,RDD数据以分布式方式存储在内存中吗?
没有!
这些就是原因:
Spark支持两种类型的共享变量:广播变量(可用于在所有节点上的内存中缓存值)和累加器(累加器)是仅“加”到变量的变量,例如计数器和总和。
RDD支持两种类型的操作:转换(从现有操作创建新的数据集)和动作(在操作数据集上进行计算后,将值返回给驱动程序)。例如,map是一种转换,它将每个数据集元素通过一个函数传递,并返回代表结果的新RDD。另一方面,reduce是使用某些函数聚合RDD的所有元素并将最终结果返回给驱动程序的操作(尽管也有并行的reduceByKey返回分布式数据集)。
Spark中的所有转换都是惰性的,因为它们不会立即计算出结果。相反,他们只记得应用于某些基本数据集(例如文件)的转换。仅当动作要求将结果返回给驱动程序时才计算转换。这种设计使Spark可以更有效地运行-例如,我们可以认识到通过map创建的数据集将用于reduce中,并且仅将reduce的结果返回给驱动程序,而不是将较大的maped数据集返回给驱动程序。
默认情况下,每次在其上执行操作时,可能会重新计算每个转换后的RDD。但是,您也可以使用persist(或缓存)方法将RDD保留在内存中,在这种情况下,Spark会将元素保留在群集中,以便下次查询时可以更快地进行访问。还支持将RDD持久保存在磁盘上,或在多个节点之间复制。
有关更多详细信息,请查看Spark编程指南。