1.RDD
RDD是Spark的最基本抽象,是对分布式内存的抽象使用,实现了以操作本地集合的方式来操作分布式数据集的抽象实现。RDD是Spark最核心的东西,它表示已被分区,不可变的并且能够被并行操作的数据集合,不同的数据集格式对应不同的RDD实现。RDD必须是可序列化的。
RDD可以cache到内存中,每次对RDD数据集的操作之后的结果,都可以存放到内存中,下一个操作可以直接从内存中输入,省去了MapReduce大量的磁盘IO操作。这对于迭代运算比较常见的机器学习算法,交互式数据挖掘来说,效率提升比较大。
(1)RDD的特点
-
它是在集群节点上的不可变的、已分区的集合对象。
-
通过并行转换的方式来创建如map,filter,join,etc。
-
失败自动重建。
-
可以控制存储级别(内存、磁盘等)来进行重用。
-
必须是可序列化的。
-
是静态类型
(2)RDD的好处
-
RDD只能从持久存储或通过Transformtions操作产生,相比于分布式共享内存(DSM)可以更高效实现容错,对于丢失部分数据分区只需根据它的lineage就可以重新计算出来,而不需要做特定的Checkpoint。
-
RDD的不变形,可以实现推测式执行
-
RDD的数据分区特性,可以通过数据的本地性来提高性能,这与Hadoop MapReduce是一样的。
-
RDD都是可序列化的,在内存不足时可自动降级为磁盘存储,把RDD存储于磁盘上,这时性能会有大的下降单不会差于现在的MapReduce。
(3)RDD的存储与分区
-
用户可以选择不同的存储级别以便重用。
-
当前RDD默认是存储于内存,但当内存不足时,RDD会spill到disk。
-
RDD在需要进行分区把数据分布于集群中时会根据每条记录Key进行分区(如Hash分区),以此保证两个数据集在Join时能高效。
(4)RDD的内部表示
在RDD的内部实现中每个RDD都可以使用5个方面的特性来表示:
-
分区列表(数据块列表)
-
计算每个分片的函数(根据父RDD计算出此RDD)
-
对父RDD的依赖列表
-
对key-value RDD的Partitioner
-
每个数据分片的预定义地址列表
(5)存储级别
RDD根据useDisk、useMemory、deserialized、replication四个参数的组合
2.RDD创建
-
通过读取外部数据集
-
通过读取集合对象
-
通过已有的RDD生成新的RDD
3.RDD操作
对于RDD可以有两种计算方式:
- 转换(返回值还是一个RDD)
- 行动(返回值不是一个RDD)
(1)转换操作(Transformations)
如map,filter,groupBy,join等,Transformations操作是Lazy的(惰性),也就是说从一个RDD转换生成另一个RDD的操作不是马上执行,Spark在遇到Transformations操作时只会记录需要这样的操作,并不会去执行,需要等到有Actions操作的时候才会真正启动计算过程进行计算。
(2)行动操作(Actions)
如:collect,count,save等,Actions操作会把返回结果或把RDD数据写到存储系统中。Actions是触发Spark启动计算的动因。
4.RDD操作实例
(1)单RDD转换函数
以{1,2,3,3}为例,f代表函数
函数名
|
目的
|
示例
|
结果
|
map(f)
|
将函数应用于每一个元素中,返回值构成新的RDD
|
rdd.map(x=>x+1)
|
{2,3,4,4}
|
flatMap(f)
|
将函数应用于每一个元素中,并把元素中迭代器内所有内容一并生成新的RDD,常用于切分单词
|
rdd.flatMap(x=>x.to(3))
|
{1,2,3,2,3,3,3}
|
filter(f)
|
过滤元素
|
rdd.filter(x=>x!=1)
|
{2,3,3}
|
distinct()
|
元素去重
|
rdd.distinct()
|
{1,2,3}
|
sample( withReplacement, fraction , [seed] )
|
元素采样,以及是否需要替换
|
rdd.sample(false,0.5)
|
不确定值,不确定数目
|
(2)集合转换操作
以{1,2,3}{3,4,5}为例,rdd代表已生成的RDD实例
函数名
|
目的
|
示例
|
结果
|
union(rdd)
|
合并两个RDD所有元素(不去重)
|
rdd1.union(rdd2)
|
{1,2,3,3,4,5}
|
intersection(rdd)
|
求两个RDD的交集
|
rdd1.intersection(rdd2)
|
{3}
|
substract(rdd)
|
移除在RDD2中存在的RDD1元素
|
rdd1.substract(rdd2)
|
{1,2}
|
cartesian(rdd)
|
求两个RDD的笛卡尔积
|
rdd1.cartesian(rdd2)
|
{(1,3),(1,4),(1,5)...(3,5)}
|
5.宽依赖和窄依赖
不同的操作依据其特性,可能会产生不同的依赖RDD之间的依赖关系有以下两种:
(1)窄依赖
一个父RDD分区最多被一个子RDD分区引用,表现为一个父RDD的分区对应于一个子RDD的分区或多个父RDD的分区对应于一个子RDD的分区,也就是说一个父RDD的一个分区不可能对应一个子RDD的多个分区,如map,filter,union等操作则产生窄依赖。
(2)宽依赖
一个子RDD的分区依赖于父RDD的多个分区或所有分区,也就是说存在一个父RDD的一个分区对应一个子RDD的多个分区,如groupByKey等操作则产生宽依赖操作。