Spark总结之RDD(六)
1. 背景
- Spark作为大数据分布式处理引擎,在设计思想上很大参考了mapreduce的设计思想,但在编程便利性上做了更高层级的抽象,屏蔽了很多分布式计算的细节。具体体现在编程接口的抽象设计上,如RDD、dataSet、dataFrame、DStream等
- Spark本身分为SaprkCore,包含RDD、Accumulators、broadCast,以及内部运行机制,在此之上,有更高层级的抽象,如Spark SQL、Spark Streaming、MLib、Graphx等四大部分,分别是sql、流式处理、机器学习、图计算。
- http://spark.apache.org/

- RDD属于Spark最基础的计算抽象,更上层的编程本质还是转换为RDD来处理,RDD再转换为物理执行计划,分布式运行。
2. RDD操作类型
- 本文主要关于RDD的数据缓存处理,spark对比mapreduce,主要体现在数据缓存上
- mapreduce由于出现比较早,21世纪初期,那时候计算机硬件还比较昂贵,特别是CPU和内存,所以为了保证数据稳定性,mapreduce的计算过程中会对数据做很多落地到磁盘的操作,通过这种方式来存储中间数据。因为当时内存宝贵,无法支撑太多的中间数据结果存储,所以主要的中间结果都是落地到磁盘,这样就会让计算性能受限于磁盘和网络IO(分布式集群中节点之间的数据存储和传输)
- spark对比mapreduce,吸收了其优点,也有shuffle操作来对数据做预期规则内的数据打散与划分,但除了shuffle之外,中间结果数据一般都缓存在内存内。因为spark出现时,已经是2009年,等到技术成熟时,已经是2014年,那时候计算机硬件已经有了一定发展,可以支持数据做内存级别缓存。
- 不过实际上,spark的数据缓存,还是可以设置的,可以选择将中间结果数据缓存在内存,硬盘,内存硬盘混合模式并设置数据副本数量(数据副本一般存储在不同节点机器的内存或者硬盘上防止单节点故障),甚至缓存在分布式存储系统如hdfs,s3n等。
- 但数据缓存本身是有代价的,需要消耗内存,硬盘存储空间资源,CPU计算资源。因为数据缓存需要对数据做处理。
- 如果数据并不是使用多次,或者数据量太大,都不建议使用缓存,因为这样带来收益较低。只有数据量相对较少,并且在后续计算中会使用多次时,使用缓存才比较合适,收益能够覆盖成本。
- 特殊情况,原始数据起始并不适合缓存,一般都是对数据做清洗和处理分析后,再缓存,这样可以降低缓存的空间资源消耗。如果真的需要对原始数据缓存,也可以换一种思路,让数据重新计算一次也是可以的。
2.1 cache
- 顾名思义,针对RDD做数据缓存,本质就是对RDD所操作产生的结果数据集合做缓存。

- 运行结果:
http://linux101:4040/jobs/

- 这里可以看到,因为使用了reduceBykey,产生了shuffle,所以有2个stage,但是由于第一个结果集合使用了cache操作,所以对应数据集合被缓存起来。
- 下次再使用这个RDD的结果数据集合时,数据就会有缓存,同时从stage来看,后续的操作不再是2个stage,因为使用了缓存,不再需要再次计算一遍,使用缓存数据即可,所以skip了一个stage,只剩下reduceBykey的后一个stage
- 从耗时来看,数据缓存之后,数据处理耗时也大大降低,从秒级别降低到了毫秒级别。
2.2 persist
- 顾名思义,这就是缓存的意思,cache本身也是调用这个persisit

object PersisitTest {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setAppName("PersisitTest")
.setMaster("local")
val sc = new SparkContext(conf)
val rdd1: RDD[String] = sc.textFile("hdfs://linux100:8020/spark/b.txt")
val words: RDD[String] = rdd1.flatMap(_.split("\\s+"))
val wordAndOne: RDD[(String, Int)] = words.map((_, 1))
wordAndOne.persist(StorageLevel.MEMORY_AND_DISK_SER)
val reducedRDD: RDD[(String, Int)] = wordAndOne.reduceByKey(_ + _)
println("reducedRDD: " + reducedRDD.collect().toBuffer)
println("reducedRDD2: " + reducedRDD.collect().toBuffer)
println("reducedRDD3: " + reducedRDD.collect().toBuffer)
Thread.sleep(100000)
sc.stop()
}
}
- 运行结果
reducedRDD: ArrayBuffer((d,53), (e,1), (a,46), (b,1), (f,1), (g,1), (c,14))
reducedRDD2