Spark-RDD简介以及算子实例
一、RDD概述
1.1 什么是RDD?
RDD(Resilient Distribute Dataset)叫做分布式数据集。式Spark最基本的数据抽象。
它代表一个不可变,可分区,里面的元素可并行计算的集合。
RDD具有数据流模型的特点:自动容错、位置感应性调度和可伸缩性。
RDD运行多个用户在执行多个查询时显示的将工作集缓存在内存中,后续的查询能够重用工作集,极大的提升了查询速度。
1.2 RDD的属性
(1)一片分组(Partition)即数据集的基本组成单位。
对于RDD来说,每个分片都会被一个计算任务处理,并决定并行计算的粒度。
用户可以在创建RDD时指定RDD分片数目。如果没有指定,就会采用默认值。
默认值就是程序所分配到cpu core的数目。
(2)计算每个分区的函数
Spark中RDD的计算一分片为单位,每个RDD都会实现compute函数以达到这个目的。
compute函数会对迭代器进行复合。不需要保存每个计算的结果。
(3)RDD之间的依赖关系
RDD的每次转换都会生成一个新的RDD,所有RDD之间就会形成类似于流水线一样的前后依赖关系。在部分分区数据丢失时,Spark可以通过这个依赖关系重新计算丢失的分区数据,而不是对RDD所有分区进行计算。
(4)partitioner,RDD的分片函数
当前Spark中实现两种类型的分片函数。一个是基于hashPartitioner。
另外一个是基于范围的RangePartitioner。
只有对于Key-Value的RDD,才会有Partitioner,非key-value的RDD的Partitioner的值为none。
(5)一个列表,存储、存取每个partition的优先位置(preferred location)
对于一个Hdfs而言,这个列表保存的就是每个partition所在的块的位置。
按照“移动数据不如移动计算”的理论,Spark 在进行任务调度的时候,会尽可能的将计算任务分配到其所要处理数据块的存储位置。
二、两种生成RDD的方式
2.1 使用textFile方法,通过本地文件或HDFS创建RDD
val rdd01 = sc.textFile("hdfs://centos01:9000/ws")
rdd01.collect
2.2 并行化的生成RDD,调用SparkContext中的parallelize()方法
val arr = Array(1,2,3,4,5)
val rdd2 = sc.parallelize(arr)
rdd2.collect
三、RDD的两种类型
3.1 举例说明:
map
是一个转换,它通过一个函数传递每个数据集元素,并返回一个表示结果的新RDD。另一方面,reduce
是一个使用某个函数聚合RDD的所有元素并将最终结果返回给驱动程序的动作(尽管还有一个reduceByKey
返回分布式数据集的并行)。
3.2 详细解说:
(1)transformation--转换类型
主要做得就是将一个已有的RDD生成另外一个RDD。transformation具有Lazy特性,transformation不会真正被执行。只有当我们的的程序遇到一个action算子的时候,代码才会被执行。这种设计让Spark 更加有效率的执行。
常用的Transformation:
转换 | 含义 |
map(func) | 返回一个新的RDD,该RDD由每一个输入元素经过func函数转换后组成 |
filter(func) | 返回一个新的RDD,该RDD由经过func函数计算后返回值为true的输入元素组成 |
flatMap(func) | 类似于map,但是每一个输入元素可以被映射为0或多个输出元素(所以func应该返回一个序列,而不是单一元素) |
mapPartitions(func) | 类似于map,但独立地在RDD的每一个分片上运行,因此在类型为T的RDD上运行时,func的函数类型必须是Iterator[T] => Iterator[U] |
mapPartitionsWithIndex(func) | 类似于mapPartitions,但func带有一个整数参数表示分片的索引值,因此在类型为T的RDD上运行时,func的函数类型必须是 (Int, Interator[T]) => Iterator[U] |
sample(withReplacement, fraction, seed) | 根据fraction指定的比例对数据进行采样,可以选择是否使用随机数进行替换,seed用于指定随机数生成器种子 |
union(otherDataset) | 对源RDD和参数RDD求并集后返回一个新的RDD |
intersection(otherDataset) | 对源RDD和参数RDD求交集后返回一个新的RDD |
distinct([numTasks])) | 对源RDD进行去重后返回一个新的RDD |
groupByKey([numTasks]) | 在一个(K,V)的RDD上调用,返回一个(K, Iterator[V])的RDD |
reduceByKey(func, [numTasks]) | 在一个(K,V)的RDD上调用,返回一个(K,V)的RDD,使用指定的reduce函数,将相同key的值聚合到一起,与groupByKey类似,reduce任务的个数可以通过第二个可选的参数来设置 |
aggregateByKey(zeroValue)(seqOp, combOp, [numTasks]) | 先按分区聚合 再总的聚合 每次要跟初始值交流 例如:aggregateByKey(0)(_+_,_+_) 对k/y的RDD进行操作 |
sortByKey([ascending], [numTasks]) | 在一个(K,V)的RDD上调用,K必须实现Ordered接口,返回一个按照key进行排序的(K,V)的RDD |
sortBy(func,[ascending], [numTasks]) | 与sortByKey类似,但是更灵活 第一个参数是根据什么排序 第二个是怎么排序 false倒序 第三个排序后分区数 默认与原RDD一样 |
join(otherDataset, [numTasks]) | 在类型为(K,V)和(K,W)的RDD上调用,返回一个相同key对应的所有元素对在一起的(K,(V,W))的RDD 相当于内连接(求交集) |
cogroup(otherDataset, [numTasks]) | 在类型为(K,V)和(K,W)的RDD上调用,返回一个(K,(Iterable<V>,Iterable<W>))类型的RDD |
cartesian(otherDataset) | 两个RDD的笛卡尔积 的成很多个K/V |
pipe(command, [envVars]) | 调用外部程序 |
coalesce(numPartitions) | 重新分区 第一个参数是要分多少区,第二个参数是否shuffle 默认false 少分区变多分区 true 多分区变少分区 false |
repartition(numPartitions) | 重新分区 必须shuffle 参数是要分多少区 少变多 |
repartitionAndSortWithinPartitions(partitioner) | 重新分区+排序 比先分区再排序效率高 对K/V的RDD进行操作 |
foldByKey(zeroValue)(seqOp) | 该函数用于K/V做折叠,合并处理 ,与aggregate类似 第一个括号的参数应用于每个V值 第二括号函数是聚合例如:_+_ |
combineByKey | 合并相同的key的值 rdd1.combineByKey(x => x, (a: Int, b: Int) => a + b, (m: Int, n: Int) => m + n) |
partitionBy(partitioner) | 对RDD进行分区 partitioner是分区器 例如new HashPartition(2 |
cache | RDD缓存,可以避免重复计算从而减少时间,区别:cache内部调用了persist算子,cache默认就一个缓存级别MEMORY-ONLY ,而persist则可以选择缓存级别 |
persist | |
|
|
Subtract(rdd) | 返回前rdd元素不在后rdd的rdd |
leftOuterJoin | leftOuterJoin类似于SQL中的左外关联left outer join,返回结果以前面的RDD为主,关联不上的记录为空。只能用于两个RDD之间的关联,如果要多个RDD关联,多关联几次即可。 |
rightOuterJoin | rightOuterJoin类似于SQL中的有外关联right outer join,返回结果以参数中的RDD为主,关联不上的记录为空。只能用于两个RDD之间的关联,如果要多个RDD关联,多关联几次即可 |
subtractByKey | substractByKey和基本转换操作中的subtract类似只不过这里是针对K的,返回在主RDD中出现,并且不在otherRDD中出现的元素 |
(2)action--动作类型
可以出发代码的运行,而Spark的运行必须要有一个action类型操作。
动作 | 含义 |
reduce(func) | 通过func函数聚集RDD中的所有元素,这个功能必须是课交换且可并联的 |
collect() | 在驱动程序中,以数组的形式返回数据集的所有元素 |
count() | 返回RDD的元素个数 |
first() | 返回RDD的第一个元素(类似于take(1)) |
take(n) | 返回一个由数据集的前n个元素组成的数组 |
takeSample(withReplacement,num, [seed]) | 返回一个数组,该数组由从数据集中随机采样的num个元素组成,可以选择是否用随机数替换不足的部分,seed用于指定随机数生成器种子 |
takeOrdered(n, [ordering]) |
|
saveAsTextFile(path) | 将数据集的元素以textfile的形式保存到HDFS文件系统或者其他支持的文件系统,对于每个元素,Spark将会调用toString方法,将它装换为文件中的文本 |
saveAsSequenceFile(path) | 将数据集中的元素以Hadoop sequencefile的格式保存到指定的目录下,可以使HDFS或者其他Hadoop支持的文件系统。 |
saveAsObjectFile(path) |
|
countByKey() | 针对(K,V)类型的RDD,返回一个(K,Int)的map,表示每一个key对应的元素个数。 |
foreach(func) | 在数据集的每一个元素上,运行函数func进行更新。 |
aggregate | 先对分区进行操作,在总体操作 |
reduceByKeyLocally |
|
lookup |
|
top |
|
fold |
|
foreachPartition |
|
四、关于RDD算子的小练习
已经将所有的题目要求并打印数据都包含在scala类当中了,可以直接进行拷贝,查看打印结果。
package day06
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
class SparkRDDTest {
}
/**
* Spark 内算子进行 练习。
*/
object SparkRDDTest{
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setAppName("SparkRDDTest").setMaster("local")
val context: SparkContext = new SparkContext(conf)
/* 一、
1,通过并行生成rdd1
2,对rdd1的每一个元素*2进行排序
3, 过滤》= 10 的元素
4,将元素以数组的方式打印出来
*/
val rdd1: RDD[Integer] = context.parallelize((List(1,3,5,7,9,0,2,4,6,8)))
val res2: RDD[Int] = rdd1.map(_ * 2).sortBy(x => x,true)
val res3: RDD[Int] = res2.filter(_ >=10)
println("对RDD的每一个元素*2进行排序 : "+res2.collect().toBuffer)
println("过滤 >= 10 的元素 : "+res3.collect().toBuffer)
/* 二、
1,通过并行生成rdd2
2,对rdd2先切分,后压平。
*/
val rdd2: RDD[String] = context.parallelize(Array("1 2 3","z x c","q,w,e"))
val res4: RDD[String] = rdd2.flatMap(_.split(" "))
println("对rdd2先切分,后压平 : "+res4.collect().toBuffer)
/* 三 、
1,通过并行生成rdd3(稍微复杂一点)
2,对rdd3先切分,后压平。
*/
val rdd3: RDD[List[String]] = context.parallelize(List(List("q w e","a s d","t i o"),List("q w q","r s d","r i o"),List("q w e","a s d","t i o")))
val res5: RDD[String] = rdd3.flatMap(_.flatMap(_.split(" ")))
println("对res5先切分,后压平 : "+res5.collect().toBuffer)
/* 四、
1,建立 rdd4 rdd5
2,求并集 ,求交集 ,去重
*/
val rdd4: RDD[Int] = context.parallelize(List(1,3,5))
val rdd5: RDD[Int] = context.parallelize(List(2,4,5))
val res6: RDD[Int] = rdd4 union rdd5
val res7: RDD[Int] = rdd4 intersection(rdd5)
val res8: RDD[Int] = res6 distinct() //先合并,后去重(直接调用方法即可)
println("求并集 : "+res6.collect().toBuffer)
println("求交集 : "+res7.collect().toBuffer)
println("去重 : "+res8.collect().toBuffer)
/* 五、
1,建立 rdd6 rdd7
2,求join
3,求左连接和右连接。
4,求并集,进行分组
5,分别用groupByKey和reduceByKey实现单词计数,注意groupByKey和reduceByKey的区别
6,cogroup 进行统计
7, 按Value降序排列
8,笛卡尔积
*/
val rdd6: RDD[(String, Int)] = context.parallelize(List(("tom1",1),("join1",4),("tom3",3)))
val rdd7: RDD[(String, Int)] = context.parallelize(List(("join1",4),("tom1",5),("join3",6)))
val res9: RDD[(String, (Int, Int))] = rdd6 join rdd7
val res10: RDD[(String, (Int, Option[Int]))] = rdd6.leftOuterJoin(rdd7)
val res11: RDD[(String, (Option[Int], Int))] = rdd6.rightOuterJoin(rdd7)
val res12: RDD[(String, Iterable[Int])] = rdd6.union(rdd7).groupByKey()
val res13: RDD[(String, Int)] = rdd6.union(rdd7).groupByKey().mapValues(_.sum)
val res14: RDD[(String, Int)] = rdd6.union(rdd7).reduceByKey(_+_)
val res15: RDD[(String, (Iterable[Int], Iterable[Int]))] = rdd6.cogroup(rdd7)
val res16: RDD[(String, Int)] = res14.map(t =>(t._2,t._1)).sortByKey(false).map(t =>(t._2,t._1))
val res18: RDD[((String, Int), (String, Int))] = rdd6.cartesian(rdd7)
println("2,join : "+res9.collect().toBuffer)
println("3,左连接 : "+res10.collect().toBuffer)
println("3,右连接 : "+res11.collect().toBuffer)
println("4,并集 进行分组 : "+res12.collect().toBuffer)
println("5,groupByKey 实现单词计数 : "+res13.collect().toBuffer)
println("5,reduceByKey 实现单词计数 : "+res14.collect().toBuffer)
println("6,cogroup 进行统计 : "+res15.collect().toBuffer)
println("7,按Value降序排列 : "+res16.collect().toBuffer)
println("7,笛卡尔积 : "+res18.collect().toBuffer)
/* 六、
1,建立 rdd8
2,reduce 聚合
*/
val rdd8: RDD[Int] = context.parallelize(List(1,2,4,6,8,3))
val res17: Int = rdd8.reduce(_+_)
println("2,reduce 聚合 : "+res17)
}
}
欢迎订阅关注公众号(JAVA和人工智能)
获取更多免费书籍、资源、视频资料
文章超级链接:
4,IntelliJ IDEA(最新)安装-破解详解--亲测可用
8,Spark-集群安装、部署、启动、测试(1.6.3)稳定版
9,Scala +Spark+Hadoop+Zookeeper+IDEA实现WordCount单词计数(简单实例--上)