1. Spark的RDD

RDD(Resilient Distributed Datasets),弹性分布式数据集,是对分布式数据集的一种抽象。
RDD所具备5个主要特性:
  • 一组分区列表
  • 计算每一个数据分片的函数
  • RDD上的一组依赖
  • 对于Key Value 对的RDD,会有一个Partitioner, 这是数据的分区器,控制数据分区策略和数量
  • 一组Preferred Location信息(如HDFS 上的数据块地址)
上图是一个简单的CoGroupedRDD满足了RDD 5个特性
 

2. RDD的两种操作

2.1 Transformation

Transformation: 转换,从现有的数据集创建一个新的数据集,从一个RDD转换成另一个RDD,transformation的操作是延迟计算的,在Driver层就构建好RDD之间的关系,数据分区策略,但并不提交计算。
Transformations 按照数据类型纬度分为:Value数据类型和Key-Value的数据类型的Transformation

2.1.1 Value型Transformation

针对以Value为输入值的RDD,常见的Map, FlatMap....,而输出值并不一定是value,也有可能是Key,Value的数据类型
以输入分区和输出分区的数据关系类型
  1. 输入分区和输出分区1对1 例如 map
  2. 输入分区和输出分区多对1 例如 union
  3. 输入分区和输出分区多对多 例如 groupBy
  4. 输入分区包含输出分区 例如 filter

2.1.2 Key-Value型Transformation

针对Key,Value的输入类型,进行聚集,连接等操作
Spark 里处理Key,Value的输入类型有个专门的类来处理
class PairRDDFunctions[K, V](self: RDD[(K, V)])
(implicit kt: ClassTag[K], vt: ClassTag[V], ord: Ordering[K] = null)
extends Logging with Serializable {
}

2.1.2.1 RDD 转 PairRDDFunctions

会不会很奇怪,并没有继承RDD,也就是说严格意义上来说,K-V的算子并不是RDD,先看看一个例子:

line.flatMap(_.split(" "))
.map((_, ))
.reduceByKey(_+_).collect().foreach(println)

reduceByKey是一个Key-Value的算子

def map[U: ClassTag](f: T => U): RDD[U] = withScope {
val cleanF = sc.clean(f)
new MapPartitionsRDD[U, T](this, (context, pid, iter) => iter.map(cleanF))
}
在rdd.scala中map函数中,返回的类型是MapPartitionsRDD,并不是PairRDDFunctions,如何转换的?
在scala语言里有个语法叫做“scala implicit method”,在隐式转化里我们看到了定义
implicit def rddToPairRDDFunctions[K, V](rdd: RDD[(K, V)])
(implicit kt: ClassTag[K], vt: ClassTag[V], ord: Ordering[K] = null): PairRDDFunctions[K, V] = {
new PairRDDFunctions(rdd)
}
将rdd自动转为PairRDDFunctions,最后调用了算子reduceByKey

2.1.2.1 PairRDDFunctions 转 RDD

Spark的核心抽象RDD是各个组件交互的核心,也是API里的主要接口,显然不能使用对象PairRDDFunctions作为RDD之间的交互。
PairRDDFunctions的初始化的时候会带入一个RDD,这是父类的RDD
@Experimental
def combineByKeyWithClassTag[C](
createCombiner: V => C,
mergeValue: (C, V) => C,
mergeCombiners: (C, C) => C,
partitioner: Partitioner,
mapSideCombine: Boolean = true,
serializer: Serializer = null)(implicit ct: ClassTag[C]): RDD[(K, C)] = self.withScope {
require(mergeCombiners != null, "mergeCombiners must be defined") // required as of Spark 0.9.0
if (keyClass.isArray) {
if (mapSideCombine) {
throw new SparkException("Cannot use map-side combining with array keys.")
}
if (partitioner.isInstanceOf[HashPartitioner]) {
throw new SparkException("HashPartitioner cannot partition array keys.")
}
}
val aggregator = new Aggregator[K, V, C](
self.context.clean(createCombiner),
self.context.clean(mergeValue),
self.context.clean(mergeCombiners))
if (self.partitioner == Some(partitioner)) {
self.mapPartitions(iter => {
val context = TaskContext.get()
new InterruptibleIterator(context, aggregator.combineValuesByKey(iter, context))
}, preservesPartitioning = true)
} else {
new ShuffledRDD[K, V, C](self, partitioner)
.setSerializer(serializer)
.setAggregator(aggregator)
.setMapSideCombine(mapSideCombine)
}
}
当调用K-V算子的时候,可以单独指定分区器,否则算子会自己构建一个HashPartitioner的分区器而分区策略依赖输入的分片块, 通过判断数据的分区器是否和父RDD的一致,构建ShuffledRDD,MapPartitionsRDD

2.2 Action

 在前面谈到Transformation都是延迟计算的,原因也很简单,所有的计算都需要最后的结果展现,如果我不想获取结果,用于计算、保存,那么计算就没有意义了,也就不需要计算了,所以用于最后需要计算的前提是需要有Action,结果展现。
比较常见的:
  • 无输出 foreach
  • 输出到文件或者HDFS
  • Scala的集合等数据类型 collect, count
在Action中,比如collect
def collect(): Array[T] = withScope {
val results = sc.runJob(this, (iter: Iterator[T]) => iter.toArray)
Array.concat(results: _*)
}

调用SparkContext运行Job

def runJob[T, U: ClassTag](
rdd: RDD[T],
func: (TaskContext, Iterator[T]) => U,
partitions: Seq[Int],
resultHandler: (Int, U) => Unit): Unit = {
if (stopped.get()) {
throw new IllegalStateException("SparkContext has been shutdown")
}
val callSite = getCallSite
val cleanedFunc = clean(func)
logInfo("Starting job: " + callSite.shortForm)
if (conf.getBoolean("spark.logLineage", false)) {
logInfo("RDD's recursive dependencies:\n" + rdd.toDebugString)
}
dagScheduler.runJob(rdd, cleanedFunc, partitions, callSite, resultHandler, localProperties.get)
progressBar.foreach(_.finishAll())
rdd.doCheckpoint()
}
SparkContext运行Job,最终就是调用了DAG 进行job的调度,关于 DAG的具体会在后面一篇讲到

3. RDD的依赖关系

protected def getDependencies: Seq[Dependency[_]] = deps  

RDD可以通过getDependencies获取到依赖的数组

@DeveloperApi
abstract class Dependency[T] extends Serializable {
def rdd: RDD[T]
}
对Dependency来说会保存Parent 的RDD, 可以通过RDD的Dependency来获取双亲的RDD,这样就能溯源
 
依赖上整体分为Narrow 和Shuffle 两类,也有人叫窄依赖,宽依赖
 
NarrowDependency 分为三类
  1. 1对1  OneToOneDependency:  常见MapRDD
  2. 多对1 RangDependency: UnionRDD
  3. 1 对部分 PruneDependency: 裁剪
ShuffleDependency 多对多,对应的是ShuffleRDD
 
只有Transformation的RDD之间才会有Dependency,而对Action来说是并不存在Dependency
 
整个RDD的分析,构建依赖,数据分片,最后通过Action提交到DAG调度,都是在Driver的主线程完成,这时候并没有构建好Job。

Spark Core (一) 什么是RDD的Transformation和Action以及Dependency(转载)的更多相关文章

  1. 03、操作RDD(transformation和action案例实战)

    1.transformation和action介绍 Spark支持两种RDD操作:transformation和action.transformation操作会针对已有的RDD创建一个新的RDD:而a ...

  2. Spark Core(三)Executor上是如何launch task(转载)

    1. 启动任务 在前面一篇博客中(Driver 启动.分配.调度Task)介绍了Driver是如何调动.启动任务的,Driver向Executor发送了LaunchTask的消息,Executor接收 ...

  3. Spark RDD/Core 编程 API入门系列 之rdd实战(rdd基本操作实战及transformation和action流程图)(源码)(三)

    本博文的主要内容是: 1.rdd基本操作实战 2.transformation和action流程图 3.典型的transformation和action RDD有3种操作: 1.  Trandform ...

  4. spark core (二)

    一.Spark-Shell交互式工具 1.Spark-Shell交互式工具 Spark-Shell提供了一种学习API的简单方式, 以及一个能够交互式分析数据的强大工具. 在Scala语言环境下或Py ...

  5. spark RDD transformation与action函数整理

    1.创建RDD val lines = sc.parallelize(List("pandas","i like pandas")) 2.加载本地文件到RDD ...

  6. Spark学习笔记之RDD中的Transformation和Action函数

    总算可以开始写第一篇技术博客了,就从学习Spark开始吧.之前阅读了很多关于Spark的文章,对Spark的工作机制及编程模型有了一定了解,下面把Spark中对RDD的常用操作函数做一下总结,以pys ...

  7. Spark Core源代码分析: RDD基础

    RDD RDD初始參数:上下文和一组依赖 abstract class RDD[T: ClassTag]( @transient private var sc: SparkContext, @tran ...

  8. Spark Core(四)用LogQuery的例子来说明Executor是如何运算RDD的算子(转载)

    1. 究竟是怎么运行的? 很多的博客里大量的讲了什么是RDD, Dependency, Shuffle.......但是究竟那些Executor是怎么运行你提交的代码段的? 下面是一个日志分析的例子, ...

  9. Spark RDD概念学习系列之Pair RDD的transformation操作

    不多说,直接上干货! Pair RDD的transformation操作 Pair RDD转换操作1 Pair RDD 可以使用所有标准RDD 上转化操作,还提供了特有的转换操作. Pair RDD转 ...

随机推荐

  1. linux系统中RPM包的通用命名规则

    http://blog.csdn.net/kexiuyi/article/details/53292358

  2. Java精选笔记_IO流(字符输入输出流、字符文件输入输出流、字符流的缓冲区)

    字符流 Reader是字符输入流的基类,用于从某个源设备读取字符 Writer是字符输出流,用于向某个目标设备写入字符 字符流操作文件 字符输入流FileReader,通过此流可以从关联的文件中读取一 ...

  3. div位置设置

    div居中显示 margin:0 auto div中的内容居中显示 text-algin:center div靠右显示 float:right 设置div元素的右外边距 margin-right:10 ...

  4. Redis(四)-- 集群

    一.Redis适合做企业级分布式缓存集群的条件 1.Redis内置哈希槽,有16384个哈希槽(0~16383),根据CRC16算法来确定这个集群中属于哪一个服务器来处理这个请求. 2.Redis提供 ...

  5. 子Fragment获取父Fragment

    在子Fragment操作父Fragment的思路 ((FragmentRecyclerBD)FragmentAppointmentBD.this.getParentFragment()).change ...

  6. Activity、Window和View三者间的关系有一定的见解

    一.简述如何将Activity展现在手机上 Tips: Activity本身是没办法处理显示什么控件(view)的,是通过PhoneWindow进行显示的 换句话说:activity就是在造Phone ...

  7. nano100B的看门狗讲解

    看门狗定时器的用途是在软件出问题时执行系统复位功能,这可以防止系统无限期地挂起.除此之外,看门狗定时器还支持将CPU 从掉电模式唤醒的功能.看门狗定时器包含一个18 位的自由运行计数器,定时溢出间隔可 ...

  8. Effective C++ —— 让自己习惯C++(一)

    条款01 : 视C++为一个语言联邦 C++ == C(C基本语法) + Object-Oriented C++(类,封装,继承,多态……) + Template C++(泛型编程) + STL(容器 ...

  9. 如何使用微信小程序制作banner轮播图?

    在前端工程师的工作中,banner是必不可少的,那缺少了DOM的小程序是如何实现banner图的呢?如同其他的框架封装了不同的banner图的方法,小程序也封装了banner的方法,来让我一一道来: ...

  10. NSFileManager和NSFileHandle(附:获取文件大小 )

    本文转载至:http://www.cnblogs.com/pengyingh/articles/2350345.html 天牛 感谢原创作者的硕果 //file 文件操作 NSFileManager  ...