Spark版本:spark-2.3.1
官方原文:https://spark.apache.org/docs/2.3.1/rdd-programming-guide.html
RDD持久性
Spark中最重要的功能之一是在整个操作中persisting(或caching)内存中的数据集。当持久化RDD时,每个节点都会存储它在内存中计算的所有分区,并在该数据集上的其他操作(或从中派生的数据集)中重用它们。这可以使未来的action更快(通常超过10倍)。缓存是迭代算法和快速交互式使用的关键工具。
您可以将RDD标记为使用其上的persist()
或cache()
方法持久化。第一次在action中计算时,它将保存在节点的内存中。Spark的缓存是容错的 - 如果RDD的任何分区丢失,它将自动使用最初创建它的转换重新计算。此外,每个持久RDD可以使用不同的StorageLevel
进行存储,例如,允许您将数据集保存在磁盘上,将其保存在内存中,但作为序列化的Java对象(以节省空间),将其复制到节点上。这些级别通过传递一个 StorageLevel
对象(Scala, Java, Python)来设置persist()
。该cache()
方法是使用默认存储级别的简写,它是StorageLevel.MEMORY_ONLY
(将反序列化对象存储在内存中)。完整的存储级别如下:
存储级别 | 含义 |
MEMORY_ONLY | 将RDD作为反序列化的Java对象存储在JVM中。如果RDD不适合内存,则某些分区将不会被缓存,并会在每次需要时重新计算。这是默认级别。 |
MEMORY_AND_DISK | 将RDD作为反序列化的Java对象存储在JVM中。如果RDD不适合内存,请存储不适合磁盘的分区,并在需要时从中读取它们。 |
MEMORY_ONLY_SER (Java和Scala) | 将RDD存储为序列化的 Java对象(每个分区一个字节的数组)。与反序列化的对象相比,这通常更节省空间,特别是在使用 快速序列化器时,但需要更多的CPU密集型读取。 |
MEMORY_AND_DISK_SER (Java和Scala) | 与MEMORY_ONLY_SER类似,但将不适合内存的分区溢出到磁盘上,而不是每次需要时重新计算它们。 |
DISK_ONLY | 将RDD分区仅存储在磁盘上。 |
MEMORY_ONLY_2,MEMORY_AND_DISK_2等 | 与上面的级别相同,但复制两个群集节点上的每个分区。 |
OFF_HEAP(实验) | 与MEMORY_ONLY_SER类似,但将数据存储在 堆内存储器中。这需要启用堆堆内存。 |
注意: 在Python中,存储的对象将始终与Pickle库串行化,所以选择序列化级别无关紧要。Python中的可用存储级别包括MEMORY_ONLY
,MEMORY_ONLY_2
, MEMORY_AND_DISK
,MEMORY_AND_DISK_2
,DISK_ONLY
,和DISK_ONLY_2
。reduceByKey
即使没有用户的呼叫,Spark也会在洗牌操作中自动保存一些中间数据(例如)persist
。这是为了避免在洗牌过程中节点失败时重新计算整个输入。我们仍建议用户调用persist
生成的RDD,如果他们打算重用它。
选择哪个存储级别?
Spark的存储级别旨在内存使用和CPU效率之间提供不同权衡。我们建议通过以下过程来选择一个:
- 如果您的RDD适合默认存储级别(
MEMORY_ONLY
- ),请将其留在那里。这是CPU处理效率最高的选项,允许RDD上的操作尽可能快地运行。
- 如果没有,请尝试使用
MEMORY_ONLY_SER
- 并选择快速序列化库,以使对象更加节省空间,但访问速度仍然相当快。(Java和Scala)
- 除非计算数据集的函数很昂贵,否则它们会过滤大量数据,否则不要泄露到磁盘。否则,重新计算分区可能与从磁盘读取分区一样快。
- 如果要快速恢复故障(例如,如果使用Spark来为Web应用程序提供请求),请使用复制的存储级别。所有的存储级别通过重新计算丢失的数据来提供完全的容错能力,但是复制的容量可让您继续在RDD上运行任务,而无需等待重新计算丢失的分区。
删除数据
Spark会自动监视每个节点上的高速缓存使用情况,并以最近最少使用(LRU)方式删除旧数据分区。如果您想要手动删除RDD,而不是等待它退出缓存,请使用该RDD.unpersist()
方法。
以上是原文的翻译,以下是我提取出的关键信息,在实际使用RDD Persistence中有一定作用。
1、能够从分区的persist中受益的操作
- cogroup()
- groupWith(), groupByKey(), reduceByKey(),
- join(), leftOuterJoin(), rightOuter Join()
- combineByKey(), and lookup().
2、并非所有操作都需要persist
3、进行宽窄依赖划分时,需要对父RDD进行persist
4、复杂函数产生的RDD需要persist,节省时间
参考:
- https://cloudxlab.com/assessment/slide/36/adv-spark-programming/553/adv-spark-programming-understanding-persistence
- https://data-flair.training/blogs/apache-spark-rdd-persistence-caching/