一、什么是持久化操作
持久化操作就是以某种方式将一些数据或结果存储起来
二、spark中为什么要有rdd的持久化的操作
主要原因在于,如果我们相对一个RDD进行复用操作的时候,基于RDD的特性,当以rdd通过transformation转化为另外一个rdd的时候,前面的rdd就会被自动释放,此时还想在原来的rdd身上进行其它操作,需要从源头进行数据计算,这样效率自然会降低。为了能够在rdd重用的时候,直接从内存中加载相关数据,所以我们需要缓存算子(persist/cache)将rdd数据持久化到内存等等其它地方。
三、 如何实现rdd的持久化的操作
spark中对RDD的持久化操作,需要通过rdd.persist(StorageLevel)/cache方法来进行实现,使用完毕之后我们可以通过调用rdd.unPersist(blocking:Boolean) (阻塞)。
四、持久化级别
官网提供的所有的持久化级别:
下面呢我们来挨个看一下:
StorageLevel:持久化级别,spark默认的持久化级别是MEMORY_ONLY
持久化级别 | 解释 |
---|---|
MEMORY_ONLY | RDD中所有的数据都会以未经序列化的java对象的格式优先存储在内存中,如果内存不够,剩下的数据不会进行持久化。很容易出OOM=OutOfMemoryException异常。java的gc频率和对象个数成正比。gc的时候会stop-the-world。 |
MEMORY_ONLY_SER | 和MEMORY_ONLY的操作几乎一致,唯一的区别是在内存中存储的不在是未经序列化的java对象,是序列化之后的数据,rdd经过序列化之后,每一个partition就只有一个字节数组,也就是说一个partition就是一个java对象。 |
MEMORY_AND_DISK | 和MEMORY_ONLY的唯一区别在于,MEMORY_ONLY不会持久化哪些在内存中持久化的数据,MEMORY_AND_DISK会将哪些在内存中保存不下的数据保存到磁盘中。 |
MEMORY_AND_DISK_SER | 就比MEMORY_AND_DISK多了一点,存储的是序列化的java对象 |
DISK_ONLY | 磁盘存储,效率太低,一般不用 |
XXXXX_2(MEMORY_ONLY_2等等) | 是在上述所有操作的基础之上进行了一个备份。从安全、高可用的角度上考虑,如果备份所消耗的时间,比数据丢失之后从源头重新计算一遍的代价小,我们才考虑使用Xxxx_2。 |
OFF_HEAP | 非堆。上述所有的操作都会使用Spark自身的内存资源,所以为了给计算提供足够的资源,可以将持久化的数据保存到非executor中。常见的OFF_HEAP:Tachyon/Alluxio |
五、案例
🌰:
object PersistApp {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setAppName(s"${PersistApp.getClass.getSimpleName}")
.setMaster("local[*]")
val sc = new SparkContext(conf)
var start = System.currentTimeMillis()
val listRDD:RDD[String] = sc.textFile("file:///E:/data/spark/core")
listRDD.persist(StorageLevel.MEMORY_ONLY)
var retRDD:RDD[(String, Int)] = listRDD.flatMap(_.split("\\s+")).map((_, 1)).reduceByKey(_+_)
println("#######count's: " + retRDD.count())
var end = System.currentTimeMillis()
println("消耗时间:" + (end - start) + " ms")
println("-----------------------------------------")
start = System.currentTimeMillis()
retRDD = listRDD.flatMap(_.split("\\s+")).map((_, 1)).reduceByKey(_+_)
println("#######count's: " + retRDD.count())
end = System.currentTimeMillis()
println("消耗时间:" + (end - start) + " ms")
sc.stop()
}
}
运行结果:
六、如何选择一个合适的持久化
上述有好多持久化的策略,到底要怎么选择呢?
官网其实已经给出了答案了,英语能力不错的可以自行阅读一下:
总结:
尽量要让所有的操作都在内存中完成,优先尝试MEMORY_ONLY,慎防OOM,如果满足不了;退而求其次,使用MEMORY_ONLY_SER,这个策略比MEMORY_ONLY多了序列化和反序列化的操作,这是主要的性能开销,但是进行反序列化之后的数据都是基于纯内存的计算,所以序列化效率还是挺高的;退而求再次,MEMORY_AND_DISK,此时不要使用这个,既然要写入磁盘,那数据量肯定很大,所以首先应该保证在内存存储尽可能多的数据,然后再考虑磁盘,如果保证内存有尽可能多的数据,不就是序列化吗?所以说MEMORY_ONLY_SER搞不定,直接MEMEORY_AND_DISK_SER;DISK_ONLY一般不用;.XXXX_2慎用,主要考虑到的安全和高可用,只有在数据备份花费的时间小于重新计算的时间的情况下我们可以考虑使用这个持久化策略。