概述
Spark一个很重要的能力就是支持对RDD的持久化(或者说缓存),并且在多个操作可以访问这些持久化数据,从从而无需再重新计算.
使用方式
- persist
可以自由选择持久化的存储级别,将RDD持久化硬盘,内存(必须是Java可序列化类型),副本到多个节点 - cache 相当于persist的快捷方式,使用默认存储级别(StorageLevel.MEMORY_ONLY)持久化数据
持久化级别
| 级别 | 描述 |
|---|---|
| MEMORY_ONLY | 默认级别.将RDD作为一个可序列化的Java对象保存到JVM的内存中(如果内存空间不够,部分分区将不会持久化,只能在需要时再重新计算) |
| MEMORY_AND_DISK | 将RDD作为一个可序列化的Java对象保存到JVM的内存中(如果内存空间不够,部分分区将持久化到磁盘,需要时直接读取) |
| MEMORY_ONLY_SER | 将RDD序列化为一个Java对象(每个分区一个byte数组),这样可以更加节省空间(特别是fast serializer),但读取时会增加更多的CPU负担 |
| MEMORY_AND_DISK_SER | 同MEMORY_ONLY_SER,但内存空间不足时,会溢写到磁盘 |
| DISK_ONLY | 全部存储到磁盘 |
| MEMORY_ONLY_2等等 | 存储与前面相同,但每个存储会存储为两份数据(副本) |
注意
在shuffle操作中,Spark会自动的对中间结果进行缓存(哪怕你没有使用persist),但仍然建议如果你需要反复使用某个RDD时,手动的缓存它
持久化级别的选择
选择哪种存储级别,实际是在内存耗用和CPU耗用之间做一个效率的权衡.Spark有如下的建议:
- 如果MEMORY_ONLY是有效的(没有内存不够导致的溢出),那就选择MEMORY_ONLY.这是对CPU耗用最小的做法,也是运行计算最快的做法
- 否则就尝试使用MEMORY_ONLY_SER并挑选一个快速序列化库.这将会占用更小的内存控件,并且依然计算非常迅速的
- 除非重计算RDD代价非常高,或者是需要过滤大量的数据的情况下,才会考虑溢写到磁盘.因为重新计算这个RDD的耗用可能会与从磁盘上读取这个RDD耗用差不多
- 如果想要快速恢复出现异常的RDD,建议使用副本机制
持久化的删除
- Spark会自动的监控各持久化的对象的使用情况,并依据最近最少使用原则( least-recently-used (LRU))将持久化数据删除.
- RDD.unpersist()手动删除一个RDD的持久化数据.
注意
persisit(cache)与unpersisit,是RDD两个相对特殊的操作.它既不是transformation也是action
persisit是标记缓存,是Lazy的,也就是在执行persisit时不会执行缓存,而是在这个被标记缓存的RDD在被计算一次之后,才会将计算结果真正进行缓存
unpersisit删除则会立即生效,但是它本身并不是一个action
val rdd = .....
rdd.cache()
val rdd1 = rdd.map(...)
//rdd.unpersist
//如果在action之前删除缓存 则缓存永远不会起效
//因为必须在执行到take时,才会真正进行缓存
rdd1.take(10).foreach(println)
//缓存释放必须要在action之后
//另,缓存释放是可以自动的,但最好是结束之后手动释放,一个申请对一个释放
rdd.unpersist