什么是持久化?暂时的东西永久保存就是持久化,在计算机中,就是内存里的东西落到磁盘上。
概念
将RDD
持久化,持久化的单位是partition
。
因为RDD不存数据,它的数据都在partition上。
控制、持久化算子有三种,cache
,persist
,checkpoint
。cache
和persist
都是懒执行的。必须有一个action
类算子触发执行。checkpoint
算子不仅能将RDD
持久化到磁盘,还能切断RDD
之间的依赖关系。
前2个调优,第三个容错。
cache
在一些计算中,DAG有可能会很长,某些RDD可能会被多个子RDD使用,每使用一次就会触发一次完整的计算,所以可以把这个父RDD缓存,这样就不用每次都计算了
rdd.cache
- 可以在sparkUI中对应application的detail ui/storage中查看缓存情况
底层调用的是persist(StorageLevel.MEMORY_ONLY)
其中持久化级别有7种
object StorageLevel {
val NONE = new StorageLevel(false, false, false, false)
val DISK_ONLY = new StorageLevel(true, false, false, false)
val DISK_ONLY_2 = new StorageLevel(true, false, false, false, 2)
val MEMORY_ONLY = new StorageLevel(false, true, false, true)
val MEMORY_ONLY_2 = new StorageLevel(false, true, false, true, 2)
val MEMORY_ONLY_SER = new StorageLevel(false, true, false, false)
val MEMORY_ONLY_SER_2 = new StorageLevel(false, true, false, false, 2)
val MEMORY_AND_DISK = new StorageLevel(true, true, false, true)
val MEMORY_AND_DISK_2 = new StorageLevel(true, true, false, true, 2)
val MEMORY_AND_DISK_SER = new StorageLevel(true, true, false, false)
val MEMORY_AND_DISK_SER_2 = new StorageLevel(true, true, false, false, 2)
val OFF_HEAP = new StorageLevel(true, true, true, false, 1)
……
}
persist
持久化的时候,如果内存不够用咋办?可否像MR那样溢写到磁盘
可以指定持久化的级别。最常用的是MEMORY_ONLY
和MEMORY_AND_DISK
。
持久化级别如下:
上面这些带有_2
的表示有副本replication
。
序列化会把对象转成字节数组,更加节省空间。但byte[]但消耗性能,类似于压缩文件。
cache
和persist
的注意事项:
cache
和persist
都是懒执行,必须有一个action
类算子触发执行。cache
和persist
算子的返回值可以赋值给一个变量,在其他job
中直接使用这个变量就是使用持久化的数据了。持久化的单位是partition
(RDD的组成)。cache
和persist
算子后不能立即紧跟action
算子。
跑一下disk_ONLY,落盘落到哪里呢?反正不是在项目工作空间下,在哪呢?不重要,它也压根不想让你知道,因为你不需要知道,本质就是个调优手段。下次需要这个RDD的数据,会自动去找。但如果想要容错,那就需要知道存在哪呢,这就要用到容错的算子,专业的事让专业的人来干,也就是我们的checkpoint算子。
checkpoint
cache只能避免重复计算,但如果是节点宕机导致数据丢失,那cache就无效了,此时仍然需要根据lineage(血统)重新计算,如果在这之前有100个rdd,那么在要经过100次的转换,效率很低。
checkpoint的作用就是将DAG中比较重要的中间数据存储到一个高可用的地方(通常是hdfs),同时切断之前的血缘,使用数据时直接从checkpoint的目录中取,这样即安全又避免了重复计算。
all references to its parent RDDs will be removed.
执行checkpoint后这个rdd之前所有的依赖关系会被移除掉,再进行计算时,直接从这个hdfs目录中去读取数据,而不需要再根据rdd的依赖关系去重新计算。
usage
- 如果在hdfs上设置,会在指定的目录下创建1个uuid为名的目录,里面按分区保存了对应RDD分区的数据,RDD有几个分区,这个目录下就有几个文件
- 因为执行checkpoint时会从头计算,这样就跟之前的计算重复了,所以文档中推荐在执行rdd.checkpoint之前先cache,也就是rdd.cache.checkpoint
- 使用sc来设置cp目录
sc.setCheckpointDir("hdfs://master:9000/checkpoint")
- 要缓存的rdd执行rdd.checkpoint,checkpoint也是lazy的,但checkpoint不是transformation,只是RDD的1个函数
这个名字起的其实很贴切,我们以前玩单机游戏的时候,会有存档的概念,存档对应的单词就是这个词,这样我们下次就可以直接从存档处开始玩。所以
checkpoint
就意味着要落盘,可以直接从存档处开始执行。
checkpoint
将RDD
持久化到磁盘,还可以切断RDD
之间的依赖关系,也是懒执行。
执行原理:
-
当
RDD
的job
执行完毕后,会从finalRDD
从后往前回溯。 -
当回溯到某一个
RDD
调用了checkpoint
方法,会对当前的RDD
做一个标记。 -
Spark
框架会自动启动一个新的job
,重新计算这个RDD
的数据,将数据持久化到HDFS
上。如果是集群模式,必须是hdfs路径
读checkpoint方法源码
切断血缘
在RDD上的所有job都执行完之后才会另起一个job执行checkpoint的操作,所以最好先执行cache,避免重新算==》图
使用checkpoint
时常用优化手段:对RDD
执行checkpoint
之前,最好对这个RDD
先执行cache
,这样新启动的job
只需要将内存中的数据拷贝到HDFS
上就可以,省去了重新计算这一步。
demo
示例如下:
SparkConf conf = new SparkConf();
conf.setMaster("local").setAppName("checkpoint");
JavaSparkContext sc = new JavaSparkContext(conf);
sc.setCheckpointDir("./checkpoint");
JavaRDD<String> lines = sc.textFile("./NASA_access_log_Aug95");
lines.checkpoint();
lines.count();
jsc.stop();
点开cp的文件,会发现里面都是数据
如果没写setCP这句话,直接执行RDD的cp,会报错
点开官网,查看cp的章节,关于持久化级别的选择,ser、disk尽量不用,还没重新算一遍快。