RDD实践
- 1、执行过程
- 2、编程模型
- 2.1 RDD创建
- 2.2 RDD转换
- 2.3 RDD输出
- 3、RDD运行过程
1、执行过程
1、读入外部的数据源(或者内存中的集合)进行 RDD 创建;
2、RDD 经过一系列的 “转换” 操作,每一次都会产生不同的 RDD,供给下一个转换使用;
3、最后一个 RDD 经过 “行动” 操作进行处理,并输出指定的数据类型和值。
优点: 惰性调用、管道化、不需要保存中间结果。
- RDD 采用了惰性调用,即在 RDD 的执行过程中,所有的转换操作都不会执行真正的操作,只会记录依赖关系,而只有遇到了行动操作,才会触发真正的计算,并根据之前的依赖关系得到最终的结果。
RDD执行过程中的一个示例如下:
如下图所示,在输入中逻辑上生成A和C两个RDD, 经过一系列"转换操作", 逻辑上生成"F"这个RDD, 之所以说是逻辑上, 是因为这个时候计算并没有发生. Spark只是记录了RDD之间的依赖关系. 当F要进行输出时, 就会执行"行动操作". Spark才会根据RDD的依赖关系生成DAG, 并从起点开始真正的计算.
- 转换操作,并不会发生真正的计算,只是记录转换的轨迹
- 动作操作,才会触发从头到尾的真正的计算,并得到结果
2、编程模型
在Spark中,RDD被表示为对象,通过对象上的方法调用来对RDD进行转换。经过一系列的transformations定义RDD之后,就可以调用actions触发RDD的计算,action可以是向应用程序返回结果(count, collect等),或者是向存储系统保存数据(saveAsTextFile等)。 在Spark中,只有遇到action,才会执行RDD的计算(即延迟计算),这样在运行时可以通过管道的方式传输多个转换。
要使用Spark,开发者需要编写一个Driver程序,它被提交到集群以调度运行Worker,如下图所示。Driver中定义了一个或多个RDD,并调用RDD上的action,Worker则执行RDD分区计算任务。
2.1 RDD创建
在Spark中创建RDD的创建方式可以分为三种:从集合中创建RDD;从外部存储创建RDD;从其他RDD创建。
1、从集合中创建
从集合中创建RDD,Spark主要提供了两种函数:parallelize
和makeRDD
1) 使用parallelize()从集合创建
scala> val rdd=sc.parallelize(Array(1,2,3,4))
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[199] at parallelize at <console>:26
2) 使用makeRDD()从集合创建
scala> val rdd1 = sc.makeRDD(Array(1,2,3,4,5,6,7,8))
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[200] at makeRDD at <console>:26
scala> val rdd2 = sc.makeRDD(List(1,2,3,4,5,6,7,8))
rdd2: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[201] at makeRDD at <console>:26
- makeRDD多一个重载方法:重载分配一系列本地Scala集合形成一个RDD,可以为每个集合对象创建一个分区,并指定优先位置便于在运行中优化调度。
- 使用本地集合创建RDD的问题在于:由于这种方法需要用到一台机器中集合的全部数据,所以这种方式在测试和原型构造之外很少使用,一般在测试时使用
2、由外部存储系统的数据集创建
由外部存储系统的数据集创建,包括本地的文件系统,还有所有Hadoop支持的数据集,比如HDFS、Cassandra、HBase等
val rdd1=sc.textFile("文件路径")
3、从其他RDD创建
通过对现有RDD的转换来创建RDD,常见的转换算子见算子这篇博文
比如:
val rdd2=rdd1.flatMap(_.split(" "))
2.2 RDD转换
- 常见的转换算子见博文
2.3 RDD输出
- RDD输出要么以文件保存,要么以一个结果输出。
- 常见的动作算子见博文
3、RDD运行过程
通过之前对RDD概念、依赖关系和阶段划分的介绍,结合之前介绍的Spark运行基本流程,总结一下RDD在Spark架构中的运行过程:
(1) 创建RDD对象
(2) SparkContext负责计算RDD之间的依赖关系,构建DAG
(3) DAGScheduler负责把DAG图分解成多个阶段,每个阶段中包含了多个任务,每个任务会被任务调度器分发给各个工作节点(Worker Node)上的Executor去执行