全称: 弹性分布式数据集
1)有一个分片列表,就是能被切分,和Hadoop一样,能够切分的数据才能并行计算。
一组分片(partition),即数据集的基本组成单位,对于RDD来说,每个分片都会被一个计算任务处理,并决定并行计算的粒度。用户可以在创建RDD时指定RDD的分片个数,如果没有指定,那么就会采用默认值。默认值就是程序所分配到的CPU Core的数目。每个分配的存储是由BlockManager实现的,每个分区都会被逻辑映射成BlockManager的一个Block,而这个Block会被一个Task负责计算。
2)由一个函数计算每一个分片,这里指的是下面会提到的compute函数。
Spark中的RDD的计算是以分片为单位的,每个RDD都会实现compute函数以达到这个目的。compute函数会对迭代器进行复合,不需要保存每次计算的结果。
3)对其他RDD的依赖列表,依赖还具体分为宽依赖和窄依赖,但并不是所有的RDD都有依赖。
RDD的每次转换都会生成一个新的RDD,所以RDD之间就会形成类似于流水线一样的前后依赖关系。在部分分区数据丢失时,Spark可以通过这个依赖关系重新计算丢失的分区数据,而不是对RDD的所有分区进行重新计算。
4)可选: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输出时的分片数量。
5)可选:每一分片的优先计算位置,比如HDFS的block的所在位置应该是优先计算的位置。
一个列表,存储存取每个Partition的优先位置(preferred location)。对于一个HDFS文件来说,这个列表保存的就是每个Partition所在的块的位置。按照“移动数据不如移动计算”的理念,Spark在进行任务调度的时候,会尽可能地将计算任务分配到其所要处理数据块的存储位置。
论文:
https://blog.csdn.net/zhanglong_4444/article/details/84950343
https://blog.csdn.net/zhanglong_4444/article/details/84968347
话不多少直接看源码:
代码: org.apache.spark.rdd.RDD
必须要有sparkcontext
必须要有唯一的 ID 和对应的名称
可以进行持久化操作 persist
cache方法 默认持久化内存 ,实际调用的还是persist方法
def persist(): this.type = persist(StorageLevel.MEMORY_ONLY)
设置快照 checkpointRDD (非同步操作)
获取依赖关系dependencies
分区partitions
每个分区都有一个下标 index
核心方法: iterator
logInfo("数据已经持久化过,直接获取或者计算。。。。")
getOrCompute(split, context)
里面的Left 和Right 方法作用 先进性记录,后续会补全。
----------------- todo -----------------------
Compute an RDD partition or read it from a checkpoint if the RDD is checkpointing.
给定一个rdd , 通过窄依赖,递归获取其祖先, 仅通过一系列狭窄的依赖关系返回与其相关的给定RDD的祖先
给定一个返回一个rdd依赖, 但并不保证其顺序
Map filter, 和FlatMap 实质都是生成一个新的MapPartitionsRDD
val cleanF = sc.clean(f)
ClosureCleaner 类中的clean 方法 再里面就,方法就很长了。
这个类的作用是递归清理外围类中无用域,降低序列化的开销,防止不必要的不可序列化异常。 降低网络io,提高executor的内存效率
MapPartitionsRDD
去重 distinct 函数实质上是用的 reduceByKey 函数进行的数据转换
重新分区 repartition 和coalesce
repartition(numPartitions:Int):RDD[T]和coalesce(numPartitions:Int,shuffle:Boolean=false):RDD[T]
他们两个都是RDD的分区进行重新划分,repartition只是coalesce接口中shuffle为true的简易实现,(假设RDD有N个分区,需要重新划分成M个分区)
1)、N<M。一般情况下N个分区有数据分布不均匀的状况,利用HashPartitioner函数将数据重新分区为M个,这时需要将shuffle设置为true。
2)如果N>M并且N和M相差不多,(假如N是1000,M是100)那么就可以将N个分区中的若干个分区合并成一个新的分区,最终合并为M个分区,这时可以将shuff设置为false,在shuffl为false的情况下,如果M>N时,coalesce为无效的,不进行shuffle过程,父RDD和子RDD之间是窄依赖关系。
3)如果N>M并且两者相差悬殊,这时如果将shuffle设置为false,父子RDD是窄依赖关系,他们同处在一个Stage中,就可能造成spark程序的并行度不够,从而影响性能,如果在M为1的时候,为了使coalesce之前的操作有更好的并行度,可以讲shuffle设置为true。
总之:如果shuff为false时,如果传入的参数大于现有的分区数目,RDD的分区数不变,也就是说不经过shuffle,是无法将RDDde分区数变多的。
根据权重随机切分: randomSplit
union 函数
实际:
函数 sortBy 实际调用的是sortbykey函数
groupby 函数
函数: mapPartitions
foreach
foreachPartition
collect
toLocalIterator
count
上面提交的时候,实际都会运行run job 方法
reduce
saveAsTextFile
toJavaRDD
只是挑了几个重要的方法
其他方法暂时略,后期会专门对常用的函数做源码分析。。。。。。。。。。。。。。。。
val textFile = sc.textFile(args(0))