RDD 持久化
RDD持久化不仅仅指平常理解的物化,在这里还可以内存化。通过在RDD上调用persist() 或 cache()来进行持久化,并在第一次行动算子被调用时真正执行。持久化的目的是RDD重用、提速,同时提供一定的容错性,如果某个RDD出错,所需数据自动按照RDD依赖关系进行重新计算得到。
RDD持久化分为不同级别,可以持久化数据集到磁盘、可以持久化到内存(序列化成java对象是为了节约空间)、甚至可以将持久化在其他节点做副本。这些级别通过传递一个StorageLevel 对象给persist()实现。cache() 是persist()以StorageLevel.MEMORY_ONLY 为默认级别的封装。全部Storage Leve列表如下:
持久化级别 | 意义 |
---|---|
MEMORY_ONLY | 持久化RDD到内存,默认级别 |
MEMORY_AND_DISK | 持久化RDD到内存,如果内存不够,把多出的部分溢写到磁盘 |
MEMORY_ONLY_SER | 将RDD序列化为java对象(一个分区,一个字节数组)持久化到内存。内存占用比MEMORY_ONLY少,代价是反序列化时需要更多的CPU消耗 |
MEMORY_AND_DISK_SER | 与MEMORY_ONLY_SER类似,内存中的部分序列化,溢写到磁盘的部分不序列化 |
DISK_ONLY | 只持久化到磁盘 |
MEMORY_ONLY_2, MEMORY_AND_DISK_2等 | 以上级别分别加上“_2”后缀,把每个分区在两个集群节点中分别存一份,也就是说双份 |
OFF_HEAP | 堆外内存 |
Storage Level怎么选呢?
其实就是在内存使用量与CPU效率之间作取舍。
- 如果使用MEMORY_ONLY,内存足够,那么就这样吧。这时速度最好。
- 如果不行,尝试选择MEMORY_ONLY_SER并使用一个快的系列化库来持久化。这时速度较快。
- 如果重新计算代价很重,那么溢写到磁盘。有时候重新计算比读磁盘差不多或还要快,就不要溢写磁盘了,直接重算就好了
- 如果需要快的容错恢复,可选双份缓存
清除缓存
Spark定期监控每一个节点,以LRU(最近最少使用)的方式清理旧数据。手动清除,可使用RDD.unpersist()。
RDD CheckPoint
使用checkpoint操作设置检查点,对依赖链很长Job非常有用的。比如持久化的数据,在发生异常的情况下丢失,可以按照依赖关系重新计算。如果这个依赖链很长,假设要经历500个RDD转换,鬼知道什么时候算完。这时设置检查点,把重RDD放到共享文件系统(HDFS,千万不要用本地文件系统)中去,下次数据丢失时,直接从检查点开始恢复,无需计算。CheckPoint跟持久化一样,是懒执行。只有等到行动算子被调用时,才单独运行一个job来按照RDD依赖关系重新执行一次,将检查点RDD数据写到HDFS中去,也就是说要执行两次,所以一般在设置检查点之前持久化。
在进行checkpoint之前,需要设置 sc.setCheckpointDir(),否则抛异常。
RDD 持久化与CheckPoint之间的区别
区别点 | 持久化 | CheckPoint |
---|---|---|
我理解的量级 | 轻 | 重 |
是否保存依赖关系 | 保存,丢失数据可重计算 | 不保存,数据安全性由HDFS保证 |
存储位置 | 一般节点本地内存或磁盘 | HDFS或其他共享文件系统 |
数据容错 | 按照RDD依赖关系重计算,单个分区丢失仅重计算该分区即可,不用重计算全部分区 | 依赖HDFS的来保证数据高可用,包括所有分区 |
数据易失性 | 内存持久化的数据在内存不足时易被清除 | 不易失 |
数据删除时机 | Job完成时自动清除 | 保存在HDFS,需手动删除 |