spark中的RDD持久化

rdd的全称为Resilient Distributed Datasets(弹性分布式数据集)

rdd的操作有两种transfrom和action。

transfrom并不引发真正的rdd计算,action才会引发真正的rdd计算。

rdd的持久化是便于rdd计算的重复使用。

官方的api说明如下:

persist ( storageLevel=StorageLevel(FalseTrueFalseFalse1) )

Set this RDD’s storage level to persist its values across operations after the first time it is computed. This can only be used to assign a new storage level if the RDD does not have a storage level set yet. If no storage level is specified defaults to (MEMORY_ONLY_SER)


在rdd参与第一次计算后,设置rdd的存储级别可以保持rdd计算后的值在内存中。(1)
另外,只有未曾设置存储级别的rdd才能设置存储级别,设置了存储级别的rdd不能修改其存储级别。(2)
(1)的举例如下:
rdd1要经过transform1得到rdd2,然后在一个循环L内rdd2进行transform2和action1。
由于trasform操作是不会真正执行的,所以rdd1执行transform1需要在循环L第一次循环的时候触发。
如果设置了rdd1的存储级别,那么循环L的第二次循环起,只需要从rdd2开始计算就好了,而不用向第一次循环时从rdd1开始计算。
rdd的持久化操作有cache()和presist()函数这两种方式。

----------------------------------------------------------------------------------------------------------------------

Spark最重要的一个功能,就是在不同操作间,持久化(或缓存)一个数据集在内存中。当你持久化一个RDD,每一个结点都将把它的计算分块结果保存在内存中,并在对此数据集(或者衍生出的数据集)进行的其它动作中重用。这将使得后续的动作(Actions)变得更加迅速(通常快10倍)。缓存是用Spark构建迭代算法的关键。
你可以用persist()或cache()方法来标记一个要被持久化的RDD,然后一旦首次被一个动作(Action)触发计算,它将会被保留在计算结点的内存中并重用。Cache有容错机制,如果RDD的任一分区丢失了,通过使用原先创建它的转换操作,它将会被自动重算(不需要全部重算,只计算丢失的部分)。当需要删除被持久化的RDD,可以用unpersistRDD()来完成该工作。
此外,每一个RDD都可以用不同的保存级别进行保存,从而允许你持久化数据集在硬盘,或者在内存作为序列化的Java对象(节省空间),甚至于跨结点复制。这些等级选择,是通过将一个org.apache.spark.storage.StorageLevel对象传递给persist()方法进行确定。cache()方法是使用默认存储级别的快捷方法,也就是StorageLevel.MEMORY_ONLY(将反序列化的对象存入内存)。
StorageLevel有五个属性,分别是:useDisk_是否使用磁盘,useMemory_是否使用内存,useOffHeap_是否使用堆外内存如:Tachyon,deserialized_是否进行反序列化,replication_备份数目。
完整的可选存储级别如下:

Storage Level Meaning
MEMORY_ONLY Store RDD as deserialized Java objects in the JVM. If the RDD does not fit in memory, some partitions will not be cached and will be recomputed on the fly each time they're needed. This is the default level.
MEMORY_AND_DISK Store RDD as deserialized Java objects in the JVM. If the RDD does not fit in memory, store the partitions that don't fit on disk, and read them from there when they're needed.
MEMORY_ONLY_SER Store RDD as serialized Java objects (one byte array per partition). This is generally more space-efficient than deserialized objects, especially when using a fast serializer, but more CPU-intensive to read.
MEMORY_AND_DISK_SER Similar to MEMORY_ONLY_SER, but spill partitions that don't fit in memory to disk instead of recomputing them on the fly each time they're needed.
DISK_ONLY Store the RDD partitions only on disk.
MEMORY_ONLY_2, MEMORY_AND_DISK_2, etc. Same as the levels above, but replicate each partition on two cluster nodes.
OFF_HEAP (experimental) Store RDD in serialized format in Tachyon. Compared to MEMORY_ONLY_SER, OFF_HEAP reduces garbage collection overhead and allows executors to be smaller and to share a pool of memory, making it attractive in environments with large heaps or multiple concurrent applications. Furthermore, as the RDDs reside in Tachyon, the crash of an executor does not lead to losing the in-memory cache. In this mode, the memory in Tachyon is discardable. Thus, Tachyon does not attempt to reconstruct a block that it evicts from memory. If you plan to use Tachyon as the off heap store, Spark is compatible with Tachyon out-of-the-box. Please refer to this page for the suggested version pairings.
存储级别的选择
Spark的不同存储级别,旨在满足内存使用和CPU效率权衡上的不同需求。我们建议通过以下的步骤来进行选择:
•如果你的RDDs可以很好的与默认的存储级别(MEMORY_ONLY)契合,就不需要做任何修改了。这已经是CPU使用效率最高的选项,它使得RDDs的操作尽可能的快。•如果不行,试着使用MEMORY_ONLY_SER并且选择一个快速序列化的库使得对象在有比较高的空间使用率的情况下,依然可以较快被访问。

尽可能不要存储到硬盘上,除非计算数据集的函数,计算量特别大,或者它们过滤
了大量的数据。否则,重新计算一个分区的速度,和与从硬盘中读取基本差不多快。
总结:调用persist()或cache()方法使用的是MEMORY_ONLY存储级别,对于广播变量,使用的是MEMORY_AND_DISK存储级别。如果想使用其他存储级别,可以调用persist(StroageLevel)。MEMORY_AND_DISK存储级别时当内存足够时直接保存到内存队列中,当内存不足时,将释放掉不属于同一个RDD的block的内存。


一、RDD架构重构与优化是什么。

尽量去复用RDD,差不多的RDD,可以抽取为一个共同的RDD,供后面的RDD计算时,反复使用。

二、怎么做?

缓存级别:
    case "NONE" => NONE
    case "DISK_ONLY" => DISK_ONLY
    case "DISK_ONLY_2" => DISK_ONLY_2
    case "MEMORY_ONLY" => MEMORY_ONLY
    case "MEMORY_ONLY_2" => MEMORY_ONLY_2
    case "MEMORY_ONLY_SER" => MEMORY_ONLY_SER
    case "MEMORY_ONLY_SER_2" => MEMORY_ONLY_SER_2
    case "MEMORY_AND_DISK" => MEMORY_AND_DISK
    case "MEMORY_AND_DISK_2" => MEMORY_AND_DISK_2
    case "MEMORY_AND_DISK_SER" => MEMORY_AND_DISK_SER
    case "MEMORY_AND_DISK_SER_2" => MEMORY_AND_DISK_SER_2
    case "OFF_HEAP" => OFF_HEAP

使用示例:
sessionid2actionRDD = sessionid2actionRDD.persist(StorageLevel.MEMORY_ONLY());

/** 
cache就是一个特殊的默认在内存中的缓存。
Persist this RDD with the default storage level (`MEMORY_ONLY`). 
*/
def cache(): JavaPairRDD[K, V] = new JavaPairRDD[K, V](rdd.cache())

三、为什么需要重构优化RDD?

这里写图片描述

如图所示。如果rdd没有缓存。
在计算RDD3的时候,会从hdfs读取一份,到RDD1到RDD2 到RDD3 需要15分钟。
再需要计算RDD4的时候,会重新从HDFS中读取,计算, 又需要耗时15分钟。 那么总共就需要30分钟。

如果把RDD1 缓存在内存或磁盘中。
那么 要计算的时候,直接从内存或磁盘中读取RDD1 即可,不需要再次读取HDFS,以及重新计算RDD1. 这样 总时间 就只需要20分钟。 大大提升了效率。

四、公共RDD一定要实现持久化。

对于多次计算和公共的RDD,一定要进行持久化。
持久化,也就是说,将RDD的数据缓存到内存中、磁盘中,BlockManager。
以后无论对这个RDD做多少次计算,那么都直接取这个RDD的持久化的数据,比如从内存中,或者磁盘中,直接提取一份数据。

五、持久化的时候是可以进行序列化的。

如果正常将数据持久化在内存中,那么可能会导致内存占用过大,这样的话,也许会导致OOM内存溢出。

当纯内存无法支撑公共RDD数据完全存放的时候,就优先考虑,使用序列化的方式,在纯内存中存储。
将RDD的每个partion的数据,序列化成一个大的字节数组,就一个对象;
序列化后,大大减少内存的空间占用。

序列化的方式,唯一的缺点,就是,获取数据的时候,需要反序列化。

如果序列化纯内存的方式,还是导致OOM,内存溢出。
就只能考虑磁盘的方式,内存+磁盘,普通方式(持久化)
内存+磁盘 ,序列化。

六、为了数据的高可靠,而且内存充足,可以使用双副本机制,进行持久化。

持久化双副本,持久化后的一个副本,因为机器宕机了,副本丢了,就还是得重新计算一次;

持久化的每个数据单元,存储一份副本,放在其他节点上,从而进行容错。一个副本丢了,可以使用另外一个。

这种方式,仅仅针对内存资源极度充足。!


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值