Spark之RDD


面试:谈谈你对RDD的理解???它有哪些特点?RDD中compute方法会传什么重要参数进来?

RDD是什么?

RDD 让开发者大大降低开发分布式应用程序的门槛以及提高执行效率,用RDD进行开发会简单很多,后面如果用DF、DS就更简单了。
看源码里是如何描述RDD的:

源码
https://github.com/apache/spark/blob/master/core/src/main/scala/org/apache/spark/rdd/RDD.scala
/**
 * A Resilient Distributed Dataset (RDD), the basic abstraction in Spark. Represents an immutable,
 * partitioned collection of elements that can be operated on in parallel. This class contains the
 * basic operations available on all RDDs, such as `map`, `filter`, and `persist`. In addition,
 * [[org.apache.spark.rdd.PairRDDFunctions]] contains operations available only on RDDs of key-value
 * pairs, such as `groupByKey` and `join`;
 * [[org.apache.spark.rdd.DoubleRDDFunctions]] contains operations available only on RDDs of
 * Doubles; and
 * [[org.apache.spark.rdd.SequenceFileRDDFunctions]] contains operations available on RDDs that
 * can be saved as SequenceFiles.
 * All operations are automatically available on any RDD of the right type (e.g. RDD[(Int, Int)])
 * through implicit.
 *
 * Internally, each RDD is characterized by five main properties:
 *
 *  - A list of partitions
 *  - A function for computing each split
 *  - A list of dependencies on other RDDs
 *  - Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned)
 *  - Optionally, a list of preferred locations to compute each split on (e.g. block locations for
 *    an HDFS file)
 *
 * All of the scheduling and execution in Spark is done based on these methods, allowing each RDD
 * to implement its own way of computing itself. Indeed, users can implement custom RDDs (e.g. for
 * reading data from a new storage system) by overriding these functions. Please refer to the
 * <a href="http://people.csail.mit.edu/matei/papers/2012/nsdi_spark.pdf">Spark paper</a>
 * for more details on RDD internals.
 */

RDD是一个弹性式分布式数据集。
弹性:体现在它的计算上面。RDD在分布式计算的时候可以容错,某个节点挂了之后,或者某个部分的数据丢失了,可以通过RDD的机制,来进行修复。
分布式:数据可以归属在一个大的集群之上,可以拆开的分布在各个机器之上,跨节点的,不像单机的所有的数据都放在一个机器上。
计算的时候你的代码可以运行在多个节点之上,只有这样才能体现出来分布式计算框架的价值所在。只有通过分布式才能提升它的执行效率。可以想象看,如果单靠一个机器来处理1T的数据有多慢,但是如果100台机器来一起分担,把这1T的数据分成多个部分来进行处理有多快。可以进行并行的处理,并没有谁先谁后,之间没有依赖关系。
数据集:可以通过读取一个文件或者通过编程的方式来创建一个数据集,也可以通过一个数据集的转换来得到一个新的数据集。这个数据集可以被拆分成很多个分区来进行存储和计算。可以这样理解:比如HDFS上的一个文件,可以以block为单位,拆分成很多个block块来进行存储。可以理解为一个文件就是一个数据集。

④Spark里一个基本的抽象。每个框架都有一个抽象的编程模型,在spark core是基于RDD进行编程的,在spark streming里面是基于DStream进行编程的,在spark SQL里面是借助于HiveContext来编程。

⑤代表了一种不可变的,可分区的集合,RDD集合里的元素可以被分区,被分成多个部分,被拆分成一片一片的,这些被拆分后的小东西,可以以并行的方式进行操作。
不可变:RDD一旦产生,它就不可改变。比如RDDA通过一系列的操作产生了一个RDDB,那么这个RDDB肯定是个新的RDD。
元素可以被分区:就是RDD里面的元素可以被拆分成一片一片的,可以理解为HDFS上的block或者MapReduce里的InputSplit。然后这些被拆分后的一片一片的小东西,可以以并行的方式进行操作。

举例说明;
比如一个RDDA:(1,2,3,4,5,6,7,8,9)
现在被拆分成了三个分区:
存储在hadoop001机器上的Partition1:(1,2,3)
存储在hadoop002机器上的Partition2:(4,5,6)
存储在hadoop003机器上的Partition3:(7,8,9)
一个RDD是由多个Partition所构成的。
你现在如果想对RDDA:(1,2,3,4,5,6,7,8,9)里每个元素进行加1操作,那么实际上就是对上面三台机器上的Partition1、Partition2、Partition3这三个分区都进行了加1的操作。
就是说你对RDD进行操作,就是对RDD里面分区进行操作。

⑥This class contains the basic operations available on all RDDs。这个类里面包含了基本的操作,比如这些操作可以访问所有的RDD,比如map, filter, and persist等。

RDD的定义

abstract class RDD[T: ClassTag](
    @transient private var _sc: SparkContext,
    @transient private var deps: Seq[Dependency[_]]
  ) extends Serializable with Logging {

  if (classOf[RDD[_]].isAssignableFrom(elementClassTag.runtimeClass)) {
    // This is a warning instead of an exception in order to avoid breaking user programs that
    // might have defined nested RDDs without running jobs with them.
    logWarning("Spark does not support nested RDDs (see SPARK-5063)")
  }

由上面看出几点:
①抽象类 :它不能直接使用,RDD必然是由子类实现的,我们使用时直接使用其子类即可。
②Serializable 序列化
③Logging 记录日志
④T 泛型 它支持的数据类型都是不确定的,可以支持很多,不是固定的
泛型用于指定方法或类可以接受任意类型参数,参数在实际使用时才被确定,泛型可以有效地增强程序的适用性,使用泛型可以使得类或方法具有更强的通用性。
“T:classTag”:相当于动态类型,你使用时传入什么类型就是什么类型,(spark的程序的编译和运行是区分了Driver和Executor的,只有在运行的时候才知道完整的类型信息)
⑤SparkContext
⑥@transient

RDD五大特性详解☆☆☆☆☆

五大特点是要背下来的。

 * Internally, each RDD is characterized by five main properties:
 *  - A list of partitions
 *  - A function for computing each split
 *  - A list of dependencies on other RDDs
 *  - Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned)
 *  - Optionally, a list of preferred locations to compute each split on (e.g. block locations for
 *    an HDFS file)

All of the scheduling and execution in Spark is done based on these methods, allowing each RDD
to implement its own way of computing itself. Indeed, users can implement custom RDDs (e.g. for
reading data from a new storage system) by overriding these functions.
  • 1)由分区组成的。可以理解为RDD是由一系列partition构成的。
  • 2)对于计算,一个函数是作用于每个split/partition上的。比如你对RDD做了一个功能/函数/转换,它会对RDD里面的所有分片或者说分区去做一个相同的计算。
  • 3)依赖于其他的RDD
    比如RDDA=>RDDB=>RDDC=>RDDD,RDDA通过一系列的转换到了RDDD,那么RDDD会依赖于前面的。RDDA可能是加载进来的,后面的RDD是由RDDA转换来的。RDD之间会有依赖关系,每个RDD都是由分区构成,你对RDDA进行转换,变成了RDDB,那么RDDA里面的partition变成了RDDB里面的partition,如果RDDA里面的某个分区或者元素挂掉了,再加载一下就可以了。如果是窄依赖,如果RDDB里面的某个分区或者某个元素挂掉了,用RDDA转换一下就可以了。这就体现了它的一个弹性。当RDD某个分区数据丢失的时候,spark可以通过这个依赖关系重新计算分区里的分区数据,而不是对于RDD里面的所有的分区进行计算,而是某一个分区进行计算就可以了,前提是窄依赖。
  • 4)可选 对key-value的RDD做分区的,默认是hash分区。对于key-value的RDD可指定一个partitioner,告诉它如何分片;常用的有hash,range
  • 5)可选 对每个分片进行计算的时候,要运行的计算/执行最好在哪(几)个机器上运行。preferred locations:数据本地性,比如你要进行的计算/操作,最好在哪台机器上进行运行,比如你的数据在哪里,最好的就是把你的task分配到数据所在的地方去运行。这样性能会更好,因为没有数据的移动,移动计算而不是移动数据。locations为复数,是因为可能会有副本,对于一个块/partition来说,可能有多个preferred locations。比如:hadoop默认有三个位置,或者spark cache到内存是可能通过StorageLevel设置了多个副本,所以一个partition可能返回多个最佳位置。

spark中的所有调度和执行都是基于这些方法完成的,允许每个RDD实现自己的计算方式,实际上,用户可以通过重写这些函数来实现自定义的RDD。

那么Resilient Distributed Dataset (RDD) 这个三个单词,弹性分布式数据集在上面五大特性方面如何体现出来的呢?
。。。。

RDD五大特性在源码中的体现

对于如上的5个特点,对应于RDD中的5个方法

方法特性
getPartitionsthe set of partitions in this RDD
computecompute a given partition
getDependenciesreturn how this RDD depends on parent RDDs
partitionerspecify how they are partitioned
getPreferredLocationsspecify placement preferences

①一个RDD是由一系列的Partition构成

protected def getPartitions: Array[Partition]
返回值是一个数组,数组里面元素是Partition

②对一个RDD做计算,实际上是对RDD里每个分区进行计算。

def compute(split: Partition, context: TaskContext): Iterator[T]
传进来的参数是split,它是Partition类型

③RDD之间有转换,有依赖,它们之间有对应关系

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

④指定如何分区

 @transient val partitioner: Option[Partitioner] = None

⑤对每个split拿到PreferredLocations最佳位置

protected def getPreferredLocations(split: Partition): Seq[String] = Nil
传进来split,返回一个集合,集合里面元素为String,为路径
举例说明RDD

在这里插入图片描述
假定现在有个RDD,它有五个partition,分布在三个节点之上。
RDD是可以做cache操作的,它有可能存在磁盘上,也有可能存在内存里,也有可能两个都有,也有可能存在多个副本。
现在有五个partition,如果取做某个计算时,就必然有五个task。如果core够用的话,就必然是五个task并行的去计算,如果不够的话,就先跑一轮,跑完再继续。
对于partition1来说,你去执行一个计算,肯定优先在NODE1节点上执行性能更好,如果在NODE2节点上执行,需要先把partition1的数据传输到NODE2上,再进行执行计算。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值