RDD编程模型
Spark操作的数据模型是RDD(分布式弹性数据集);
RDD和普通数据结构ArrayList的区别
RDD为逻辑概念,在内存中不会为某个RDD分配存储空间(除非在RDD被缓存),RDD的数据只能在中间产生,计算完成后会消失;
ArrayList等数据结构会常驻内存
RDD包含不同的分区,不同的分区可以由不同的Task在不同节点处理
RDD数据之间的依赖关系如何确定的?
通过ParentRDD 的分区和child分区的对应关系;
数据依赖关系分类:
1.窄依赖:如果生成的childRdd每个分区都依赖parentRdd中的一部分分区,那么这个分区依赖关系称为窄依赖
OneToOne:一对一依赖childRDD一个分区对应parentRDD一个分区,子父分区个数相同,对应tranform为 map,filter等
RangeDependency(区域依赖):表示child RDD和parent RDD的分区经过区域化后存在一一映射关系。 典型的transformation() 包括union() 等
多对一依赖(ManyToOneDependency) : 表示child RDD中的一个分区同时依赖多个parent RDD中的分区。 典型的transformation() 包括具有特殊性质的cogroup(),join()等
多对多依赖(ManyToManyDependency) : 表示child RDD中的一个分区依赖parent RDD中的多个分区, 同时parent RDD中的一个分区被child RDD中的多个分区依赖。 典型的transformation() 是cartesian()。
2.宽依赖:新生成的child RDD中的分区依赖parent RDD中的每个分区的一部分
RDD内部的数据划分
(1) 水平划分: 按照record的索引进行划分。 例如, 我们经常使用的sparkContext.parallelize(list(1, 2, 3, 4, 5,6,7, 8, 9) , 3) ,就是按照元素的下标划分, (1, 2, 3) 为一组, (4, 5, 6) 为一组,(7, 8, 9) 为一组。 这种划分方式经常用于输入数据的划分, 如使用Spark处理大数据时, 我们先将输入数据上传到HDFS上, HDFS自动对数据进行水平划分, 也就是按照128MB为单位将输入数据划分为很多个小数据块(block) , 之后每个Spark task可以只处理一个数据块。
(2) Hash划分(HashPartitioner) : 使用record的Hash值来对数据进行划分, 该划分方法的好处是只需要知道分区个数, 就能将数据确定性地划分到某个分区。 在水平划分中, 由于每个RDD中的元素数目和排列顺序不固定, 同一个元素在不同RDD中可能被划分到不同分区。 而使用HashPartitioner, 可以根据元素的Hash值, 确定性地得出该元素的分区。 该划分方法经常被用于数据Shuffle阶段。
(3) Range划分(RangePartitioner) : 该划分方法一般适用于排序任务, 核心思想是按照元素的大小关系将其划分到不同分区, 每个分区表示一个数据区域。 例如, 我们想对一个数组进行排序, 数组里每个数字是[0, 100]中的随机数, Range划分首先将上下界[0, 100]划分为若干份(如10份) , 然后将数组中的每个数字分发到相应的分区, 如将18分发到(10, 20]的分区, 最后对每个分区进行排序, 这个排序过程可以并行执行, 排序完成后是全局有序的结果。 Range划分需要提前划分好数据区域, 因此需要统计RDD中数据的最大值和最小值。 为了简化这个统计过程, Range划分经常采用抽样方法来估算数据区域边界