1.RDD
RDD是spark特有的数据模型,谈到RDD就会提到什么弹性分布式数据集,什么有向无环图,本文暂时不去展开这些高深概念。
(0)随便找个点理解以下
最重要的记住,RRD是不可变的,也就是说,已有的RDD不能被修改或者更新,但可以从已有的RDD转化成一个新的RDD.
上面的特性解读:
RDD可以cache到内存中,每次对RDD数据集的操作之后的结果,都可以存放到内存中,下一个操作可以直接从内存中输入,省去了MapReduce大量的磁盘IO操作。这对于迭代运算比较常见的机器学习算法, 交互式数据挖掘来说,效率提升比较大。
下面我们来列举以下RDD的基本特性:
(1)RDD的特点
1)创建:只能通过转换 ( transformation ,如map/filter/groupBy/join 等,区别于动作 action) 从两种数据源中创建 RDD 1 )稳定存储中的数据; 2 )其他 RDD。 2)只读:状态不可变,不能修改。 3)分区:支持使 RDD 中的元素根据那个 key 来分区 ( partitioning ) ,保存到多个结点上。还原时只会重新计算丢失分区的数据,而不会影响整个系统。 4)路径:在 RDD 中叫世族或血统 ( lineage ) ,即 RDD 有充足的信息关于它是如何从其他 RDD 产生而来的。 5)持久化:支持将会被重用的 RDD 缓存 ( 如 in-memory 或溢出到磁盘 )。 6)延迟计算: Spark 也会延迟计算 RDD ,使其能够将转换管道化 (pipeline transformation)。 7)操作:丰富的转换(transformation)和动作 ( action ) , count/reduce/collect/save 等。 执行了多少次transformation操作,RDD都不会真正执行运算(记录lineage),只有当action操作被执行时,运算才会触发。123456789
(2)RDD的好处
1)RDD只能从持久存储或通过Transformations操作产生,相比于分布式共享内存(DSM)可以更高效实现容错,对于丢失部分数据分区只需根据它的lineage就可重新计算出来,而不需要做特定的Checkpoint。 2)RDD的不变性,可以实现类Hadoop MapReduce的推测式执行。 3)RDD的数据分区特性,可以通过数据的本地性来提高性能,这不Hadoop MapReduce是一样的。 4)RDD都是可序列化的,在内存不足时可自动降级为磁盘存储,把RDD存储于磁盘上,这时性能会有大的下降但不会差于现在的MapReduce。 5)批量操作:任务能够根据数据本地性 (data locality) 被分配,从而提高性能。123456
(3)RDD的内部属性
通过RDD的内部属性,用户可以获取相应的元数据信息。通过这些信息可以支持更复杂的算法或优化。 1)分区列表:通过分区列表可以找到一个RDD中包含的所有分区及其所在地址。 2)计算每个分片的函数:通过函数可以对每个数据块进行RDD需要进行的用户自定义函数运算。 3)对父RDD的依赖列表,依赖还具体分为宽依赖和窄依赖,但并不是所有的RDD都有依赖。 4)可选:key-value型的RDD是根据哈希来分区的,类似于mapreduce当中的Paritioner接口,控制key分到哪个reduce。 5)可选:每一个分片的优先计算位置(preferred locations),比如HDFS的block的所在位置应该是优先计算的位置。(存储的是一个表,可以将处理的分区“本地化”) 1234567
2. RDD操作
2.1 转化操作
RDD的转化操作是返回新RDD的操作, 常用转化操作总结如下:
表1: 对一个数据为{1,2,3,3}的RDD进行基本的转化操作
函数名 | 目的 | 示例 | 结果 |
---|---|---|---|
map() | 将函数应用于RDD中每个元素, 将返回值构成新的RDD | rdd.map(x=>x+1) | {2,3,4,5} |
flatMap() | 将函数应用于RDD中的每个元素, 将返回的迭代器的所有内容构成新的RDD, 常用来切分单词 | rdd.flatMap(x=>x.to(2)) | {1,2,2} |
filter() | 返回一个通过传入给filter()的函数的元素组成的RDD | rdd.filter(x=> x>2) | {3,3} |
distinct() | 去重 | rdd.distinct() | {1,2,3} |
sample(withReplacement, fraction, [seed]) | 对RDD采样, 以及是否替换 | rdd.sample(false, 0.5) | 非确定的 |
表2: 对数据分别为{1,2,3}和{2,3,4}RDD进行针对2个RDD的转化操作
函数名 | 目的 | 示例 | 结果 |
---|---|---|---|
redcue() | 并行整合RDD中的所有元素 | rdd.reduce((x, y) => x+y) | 9 |
collect() | 返回RDD中的所有元素 | rdd.collect() | {1,2,3,4} |
count() | 求RDD中的元素个数 | rdd.count() | 4 |
countByValue() | 各元素在RDD中出现的次数 | rdd.countByValue() | {1,1}, {2, 1}, {3,2} |
take(n) | 从RDD中返回n个元素 | rdd.take(2) | {1,2} |
top(n) | 从RDD中返回前n个元素 | rdd.top(3) | {3,3,2} |
foreach(func) | 对RDD中的每个元素使用给定的函数 | rdd.foreach(print) | 1,2,3,3 |
2.2 行动操作
RDD的行动操作会把最终求得的结果返回驱动器程序, 或者写入外部存储系统中。
表3: 对一个数据为{1,2,3,3}的RDD进行基本RDD的行动操作
函数名 | 目的 | 示例 | 结果 |
---|---|---|---|
redcue() | 并行整合RDD中的所有元素 | rdd.reduce((x, y) => x+y) | 9 |
collect() | 返回RDD中的所有元素 | rdd.collect() | {1,2,3,4} |
count() | 求RDD中的元素个数 | rdd.count() | 4 |
countByValue() | 各元素在RDD中出现的次数 | rdd.countByValue() | {1,1}, {2, 1}, {3,2} |
take(n) | 从RDD中返回n个元素 | rdd.take(2) | {1,2} |
top(n) | 从RDD中返回前n个元素 | rdd.top(3) | {3,3,2} |
foreach(func) | 对RDD中的每个元素使用给定的函数 | rdd.foreach(print) | 1,2,3,3 |