RDD(Resilient Distributed Datasets) ,弹性分布式数据集, 是分布式内存的一个抽象概念。
RDD是只读的记录分区的集合,只能通过在其他RDD执行确定的转换操作(如map、join和group by)而创建。
RDD可以看作是Spark的一个对象,运行于内存中,例如读文件是一个RDD,对文件计算是一个RDD,结果集也是一个RDD ,不同的分片、 数据之间的依赖 、key-value类型的map数据都可以看做RDD。
作用:提供了一种高度受限共享内存模型
RDD 代表者一个不可变、带有分区的集合,可以被并行操作。
1 A list of partitions(分区列表)
2 A function for computing each split(每个分区都有一个计算函数。每个分区都是独立运行function操作的,继而实现并行)
3 A list of dependencies on other RDDs(依赖于其他RDD的列表。因为RDD是不可变的,因此RDD存在一种转换依赖关系,将这种转换依赖关系称为RDD的血统-lineage,可以实现RDD的故障恢复)
4 Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned)(可以对Key-Value类型的数据,指定Partitioner策略,默认系统使用Hash-Partitioned)
5 Optionally, a list of preferred locations to compute each split on (e.g. block locations for an HDFS file)(每一个分区都有一个优先位置的列表。Spark在计算HDFS的时候,可以考虑最优计算策略。)
val inputs=sc.textFile("/user/test.txt")
val lines=inputs.flatMap(line=>line.split(" "))
rdd1.distinct() 去重
rdd1.union(rdd2) 合并
rdd1.intersection(rdd2) 交集
rdd1.subtract(rdd2) rdd1 有 rdd2没有
在RDD上计算出一个结果 把结果返回给driver program:
接收一个函数作用在RDD两个类型相同的元素上返回一个新元素。可以实现RDD中元素的累加、计数 和其他类型的聚集操作
val rdd = sc.parallelize(Array(1,2,3,3))
rdd.reduce((x,y)=>x+y)
遍历整个RDD,向driver program返回RDD内容。
在数据量比较小做测试的时候可以使用,但是数量大的时候就得使用输出到HDFS上保存查看。(saveAsTextFile() action)
返回RDD的前n个元素
遍历RDD中的每个元素,可以配合println()打印出数据
lines.foreach(println)
RDD依赖关系也称为RDD的血统,描述了RDD间的转换关系 。Spark将RDD间依赖关系分为了宽依赖|ShuffleDependency
、窄依赖|NarrowDependency
,Spark在提交任务的时候会根据转换算子逆向推导出所有的Stage。然后计算推导的stage的分区用于表示该Stage执行的并行度
。
说白了就是看转换算子在计算的时候做不做shuffer,如果做了就需要重新分区,就将一个分区中的数据分到不同的分区中了这就叫做宽依赖,其他的没有做shuffer的下一个rdd的分区就和上一个rdd的分区一模一样,那就是窄依赖
AG(Directed Acyclic Graph)叫做有向无环图,原始的RDD通过一系列的转换就就形成了DAG,根据RDD之间的依赖关系的不同将DAG划分成不同的Stage,对于窄依赖,partition的转换处理在Stage中完成计算。对于宽依赖,由于有Shuffle的存在,只能在parent RDD处理完成后,才能开始接下来的计算,因此宽依赖是划分stage的依据。
说白了就是如果是窄依赖就不会产生新的阶段(Stage),如果涉及到宽依赖,就会Shuffer,就会产生一个新的阶段(Stage)
一个RDD任务会切分分为:Application、Job、Stage和Task
Application:初始化一个SparkContext即生成一个Application
Job:一个Action算子就会生成一个Job
Stage:根据RDD之间的依赖关系的不同将Job划分成不同的Stage,遇到一个宽依赖则划分一个Stage。
Task:Stage是一个TaskSet,将Stage划分的结果发送到不同的Executor执行即为一个Task。
RDD通过persist方法或cache方法可以将前面的计算结果缓存,默认情况下 persist() 会把数据缓存在 JVM 的堆空间中。
但是并不是这两个方法被调用时立即缓存,而是触发后面的action时,该RDD将会被缓存在计算节点的内存中,并供后面重用。
通过查看源码发现cache最终也是调用了persist方法,默认的存储级别都是仅在内存存储一份,Spark的存储级别还有好多种,存储级别在object StorageLevel中定义的。
Spark中对于数据的保存除了持久化操作之外,还提供了一种检查点的机制,检查点(本质是通过将RDD写入Disk做检查点)是为了通过lineage做容错的辅助,lineage过长会造成容错成本过高,这样就不如在中间阶段做检查点容错,如果之后有节点出现问题而丢失分区,从做检查点的RDD开始重做Lineage,就会减少开销。检查点通过将数据写入到HDFS文件系统实现了RDD的检查点功能。
为当前RDD设置检查点。该函数将会创建一个二进制的文件,并存储到checkpoint目录中,该目录是用SparkContext.setCheckpointDir()设置的。在checkpoint的过程中,该RDD的所有依赖于父RDD中的信息将全部被移除。对RDD进行checkpoint操作并不会马上被执行,必须执行Action操作才能触发。
sc.setCheckpointDir("hdfs://hadoop10:9000/rdd-checkpoint")
val rdd1: RDD[String] = sc.makeRDD(List("zhangsan"))
val rdd2: RDD[String] = rdd1.map(_ + System.currentTimeMillis())
rdd2.checkpoint() //此处可以通过查看checkpoint方法的注释了解它的运行机制
rdd2.collect().foreach(println) //zhangsan1588656910532
rdd2.collect().foreach(println) //zhangsan1588656910641
rdd2.collect().foreach(println) //zhangsan1588656910641