Spark学习笔记(1)RDD

RDD

RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象,它代表一个不可变、可分区、里面的元素可并行计算的集合。RDD具有数据流模型的特点:自动容错、位置感知性调度和可伸缩性。RDD允许用户在执行多个查询时显式地将工作集缓存在内存中,后续的查询能够重用工作集,这极大地提升了查询速度。


RDD内部结构.png

RDD5大特型

A list of partitions 一个分区列表
A function for computing each split 对每个切片的计算函数
A list of dependencies on other RDDs RDD内部存放一个依赖列表(怎么变换来的)
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) 数据本地化 可选的

特性一

-- A list of partitions:
每个RDD分区都分布在集群节点上,分区的多少涉及到RDD Computing 并行效率。其实,分区只是一个逻辑概念,前后变换的分区,可能是物理机器上同一块内存或者存储,用户可以指定分区数,默认是CPU核数。如果从HDFS文件创建时数据块数。
Spark中的io也是不可避免的,但是网络传输Spark进行了优化,Spark把Rdd进行分区,放在集群上并行计算。同一个RDD分片100个,10个节点,平均每个节点10个分区,当进行sum时,网络传输非常小,各自节点将计算好的sum值shuffle到主程序进行全局sum。所以会很快,但是进行join时候需要把数据本身进行shuffle,网络开销很大。如果时做了分区,就会将相同key在同一个分区中,按key进行聚合的时就不需要再shuffle了。
RDD 分区的一个分配原则是:尽可能使得分区的个数,等于集群核心数目。

  /**
   * Implemented by subclasses to return the set of partitions in this RDD. This method will only
   * be called once, so it is safe to implement a time-consuming computation in it.
   *
   * The partitions in this array must satisfy the following property:
   *   `rdd.partitions.zipWithIndex.forall { case (partition, index) => partition.index == index }`
   */
  protected def getPartitions: Array[Partition]

特性二

--A function for computing each split:
一个函数作用在一个分区上。比如说一个分区有1,2,3 在rdd1.map(_*10),把RDD里面的每一个元素取出来乘以10,每个分片都应用这个map的函数。

 @DeveloperApi 
 def compute(split: Partition, context: TaskContext): Iterator[T]

两个参数:1.分区对象 2.TaskContext对象
compute函数会对迭代器进行复合,不需要保存每次计算的结果
Spark支持两个类型(算子)操作:Transformation和Action
主要做的是就是将一个已有的RDD生成另外一个RDD。Transformation具有lazy特性(延迟加载)。Transformation算子的代码不会真正被执行。只有当我们的程序里面遇到一个action算子的时候,代码才会真正的被执行。这种设计让Spark更加有效率地运行。

特性三

--A list of dependencies on other RDDs RDD:


RDD依赖.png

窄依赖:是指每个父RDD的一个Partition最多被子RDD的一个Partition所使用,例如map,filter,union。(独生子女)

宽依赖:是指一个父RDD的Partition会被多个子RDD的Partition所使用,例如groupBykey,reduceBykey,sortBykey等操作都会产生宽依赖。(超生)

在这里我们是从父RDD的partition被使用的个数来定义窄依赖和宽依赖,因此可以用一句话概括下:如果父RDD的一个Partition被子RDD的一个Partition所使用就是窄依赖,否则的话就是宽依赖。因为是确定的partition数量的依赖关系,所以RDD之间的依赖关系就是窄依赖;由此我们可以得出一个推论:即窄依赖不仅包含一对一的窄依赖,还包含一对固定个数的窄依赖。

一对固定个数的窄依赖的理解:即子RDD的partition对父RDD依赖的Partition的数量不会随着RDD数据规模的改变而改变;换句话说,无论是有100T的数据量还是1P的数据量,在窄依赖中,子RDD所依赖的父RDD的partition的个数是确定的,而宽依赖是shuffle级别的,数据量越大,那么子RDD所依赖的父RDD的个数就越多,从而子RDD所依赖的父RDD的partition的个数也会变得越来越多。
从后往前推,遇到宽依赖就断开,划分为一个stage;遇到窄依赖就将这个RDD加入该stage中

特性四

-- Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned) :
可选:key-value型的RDD是根据哈希来分区的,类似于mapreduce当中的paritioner接口,控制Key分到哪个reduce。
一个partitioner,即RDD的分片函数。当前Spark中实现了两种类型的分片函数,一个是基于哈希的HashPartitioner,另外一个基于范围的RangePartitioner。只有对于key-value的RDD,才会有Partitioner,非key-value的RDD的Partitioner的值是None。Partitioner函数不但决定了RDD本身的分片数量,也决定了parent RDD Shuffle输出时的分片数量。类似hadoop的预分区。

特性五

Optionally, a list of preferred locations to compute each split on (e.g. block locations for an HDFS file) :
可选:每一分片的优先计算位置,比如HDFS的block的所在位置应该是优先计算的位置。
一个列表,存储存取每个Partition的优先位置(preferred location)。对于一个HDFS文件来说,这个列表保存的就是每个Partition所在的块的位置。按照“移动数据不如移动计算”的理念,Spark在进行任务调度的时候,会尽可能地将计算任务分配到其所要处理数据块的存储位置。

 /**
   * Implemented by subclasses to return the set of partitions in this RDD. This method will only
   * be called once, so it is safe to implement a time-consuming computation in it.
   *
   * The partitions in this array must satisfy the following property:
   *   `rdd.partitions.zipWithIndex.forall { case (partition, index) => partition.index == index }`
   */
  protected def getPartitions: Array[Partition]

你可能感兴趣的:(Spark学习笔记(1)RDD)