spark-core


一、RDD概述

1.1 什么是RDD?

RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象。代码中是一个抽象类,它代表一个弹性的、不可变、可分区、里面的元素可并行计算的集合。

1.2 RDD的属性

1) 一组分区(Partition),即数据集的基本组成单位; 
2) 一个计算每个分区的函数; 3) RDD之间的依赖关系; 
4) 一个Partitioner,即RDD的分片函数; 
5) 一个列表,存储存取每个Partition的优先位置(preferred location)。 

1.3 RDD的特点

RDD表示只读的分区的数据集,对RDD进行改动,只能通过RDD的转换操作,由一个RDD得到一个新的RDD,新的RDD包含了从其他RDD衍生所必需的信息。RDDs之间存在依赖,RDD的执行是按照血缘关系延时计算的。如果血缘关系较长,可以通过持久化RDD来切断血缘关系。

1.3.1 弹性

	1)存储的弹性:内存与磁盘的自动切换; 
	2)容错的弹性:数据丢失可以自动恢复;
    3)计算的弹性:计算出错重试机制;
    4)分片的弹性:可根据需要重新分片。

1.3.2 分区

RDD逻辑上是分区的,每个分区的数据是抽象存在的,计算的时候会通过一个compute函数得到每个分区的数据。如果RDD是通过已有的文件系统构建,则compute函数是读取指定文件系统中的数据,如果RDD是通过其他RDD转换而来,则compute函数是执行转换逻辑将其他RDD的数据进行转换。

1.3.3 只读

RDD是只读的,要想改变RDD中的数据,只能在现有的RDD基础上创建新的RDD。 由一个RDD转换到另一个RDD,可以通过丰富的操作算子实现,不再像MapReduce那样只能写map和reduce了。 RDD的操作算子包括两类,一类叫做transformations,它是用来将RDD进行转化,构建RDD的血缘关系;另一类叫做actions,它是用来触发RDD的计算,得到RDD的相关计算结果或者将RDD保存的文件系统中。

1.3.4 依赖关系

RDDs通过操作算子进行转换,转换得到的新RDD包含了从其他RDDs衍生所必需的信息,RDDs之间维护着这种血缘关系,也称之为依赖。如下图所示,依赖包括两种,一种是窄依赖,RDDs之间分区是一一对应的,另一种是宽依赖,下游RDD的每个分区与上游RDD(也称之为父RDD)的每个分区都有关,是多对多的关系。
在这里插入图片描述

1.3.5 缓存

如果在应用程序中多次使用同一个RDD,可以将该RDD缓存起来,该RDD只有在第一次计算的时候会根据血缘关系得到分区的数据,在后续其他地方用到该RDD的时候,会直接从缓存处取而不用再根据血缘关系计算,这样就加速后期的重用。如下图所示,RDD-1经过一系列的转换后得到RDD-n并保存到hdfs,RDD-1在这一过程中会有个中间结果,如果将其缓存到内存,那么在随后的RDD-1转换到RDD-m这一过程中,就不会计算其之前的RDD-0了。
在这里插入图片描述

1.3.6 CheckPoint

虽然RDD的血缘关系天然地可以实现容错,当RDD的某个分区数据失败或丢失,可以通过血缘关系重建。但是对于长时间迭代型应用来说,随着迭代的进行,RDDs之间的血缘关系会越来越长,一旦在后续迭代过程中出错,则需要通过非常长的血缘关系去重建,势必影响性能。为此,RDD支持checkpoint将数据保存到持久化的存储中,这样就可以切断之前的血缘关系,因为checkpoint后的RDD不需要知道它的父RDDs了,它可以从checkpoint处拿到数据。

二、RDD编程

2.1编程模型

在Spark中,RDD被表示为对象,通过对象上的方法调用来对RDD进行转换。经过一系列的transformations定义RDD之后,就可以调用actions触发RDD的计算,action可以是向应用程序返回结果(count, collect等),或者是向存储系统保存数据(saveAsTextFile等)。在Spark中,只有遇到action,才会执行RDD的计算(即延迟计算),这样在运行时可以通过管道的方式传输多个转换。 要使用Spark,开发者需要编写一个Driver程序,它被提交到集群以调度运行Worker。Driver中定义了一个或多个RDD,并调用RDD上的action,Worker则执行RDD分区计算任务。

2.2RDD的创建

在Spark中创建RDD的创建方式可以分为三种:从集合中创建RDD;从外部存储创建RDD;从其他RDD创建。

2.2.1 从集合中创建

从集合中创建RDD,Spark主要提供了两种函数:parallelize和makeRDD
1)使用parallelize()从集合创建

scala> val rdd = sc.parallelize(Array(1,2,3,4,5,6,7,8)) 
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at <console>:24 

2)使用makeRDD()从集合创建

scala> val rdd1 = sc.makeRDD(Array(1,2,3,4,5,6,7,8))
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[1] at makeRDD at <console>:24

2.2.2 由外部存储系统的数据集创建

包括本地的文件系统,还有所有Hadoop支持的数据集,比如HDFS、Cassandra、HBase等

scala> val rdd2= sc.textFile("hdfs://hadoop102:9000/RELEASE") 
rdd2: org.apache.spark.rdd.RDD[String] = hdfs:// hadoop102:9000/RELEASE MapPartitionsRDD[4] at textFile at <console>:24 

2.2.3 从其他RDD创建

这里先暂不说明

2.3 RDD的转换

RDD整体上分为Value类型和Key-Value类型

2.3.1 value类型

2.3.1.1 map(func)案例
  1. 作用:返回一个新的RDD,该RDD由每一个输入元素经过func函数转换后组成
  2. 需求:创建一个1-10数组的RDD,将所有元素*2形成新的RDD
(1)创建 
scala> var source = sc.parallelize(1 to 10) 
source: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[8] at parallelize at <console>:24 
(2)打印 
 scala> source.collect() 
 res7: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 
(3)将所有元素*2 
scala> val mapadd = source.map(_ * 2) 
mapadd: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[9] at map at <console>:26 
(4)打印最终结果 
scala> mapadd.collect()
res8: Array[Int] = Array(2, 4, 6, 8, 10, 12, 14, 16, 18, 20) 
2.3.1.2 mapPartitions(func)案例
  1. 作用:类似于map,但独立地在RDD的每一个分片上运行,因此在类型为T的RDD上运行时,func的函数类型必须是Iterator[T] => Iterator[U]。假设有N个元素,有M个分区,那么map的函数的将被调用N次,而mapPartitions被调用M次,一个函数一次处理所有分区。
  2. 需求:创建一个RDD,使每个元素*2组成新的RDD
(1)创建一个RDD 
scala> val rdd = sc.parallelize(Array(1,2,3,4)) 
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[4] at parallelize at <console>:24 
(2)使每个元素*2组成新的RDD scala> rdd.mapPartitions(x=>x.map(_*2))
 res3: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[6] at mapPartitions at <console>:27 
(3)打印新的RDD 
scala> res3.collect res4: Array[Int] = Array(2, 4, 6, 8)
2.3.1.3 mapPartitionsWithIndex(func) 案例
  1. 作用:类似于mapPartitions,但func带有一个整数参数表示分片的索引值,因此在类型为T的RDD上运行时,func的函数类型必须是(Int, Interator[T]) => Iterator[U];
  2. 需求:创建一个RDD,使每个元素跟所在分区形成一个元组组成一个新的RDD
(1)创建一个RDD 
scala> val rdd = sc.parallelize(Array(1,2,3,4))
 rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[4] at parallelize at <console>:24
(2)使每个元素跟所在分区形成一个元组组成一个新的RDD 
scala> val indexRdd = rdd.mapPartitionsWithIndex((index,items)=>(items.map((index,_))))
indexRdd: org.apache.spark.rdd.RDD[(Int, Int)] = MapPartitionsRDD[5] at 
mapPartitionsWithIndex at <console>:26 
(3)打印新的RDD
 scala> indexRdd.collect res2: Array[(Int, Int)] = Array((0,1), (0,2), (1,3), (1,4)) 

诸如此类的function函数有很多,不一一举例。

2.3.2 双Value类型交互

2.3.2.1 union(otherDataset) 案例
 求并集

(1)创建第一个RDD
scala> val rdd1 = sc.parallelize(1 to 5) 
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[23] at parallelize at <console>:24
(2)创建第二个RDD 
scala> val rdd2 = sc.parallelize(5 to 10) 
rdd2: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[24] at parallelize at <console>:24
(3)计算两个RDD的并集
scala> val rdd3 = rdd1.union(rdd2) 
rdd3: org.apache.spark.rdd.RDD[Int] = UnionRDD[25] at union at <console>:28 
(4)打印并集结果 
scala> rdd3.collect()
res18: Array[Int] = Array(1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10) 
2.3.2.2 subtract (otherDataset) 案例
  1. 作用:计算差的一种函数,去除两个RDD中相同的元素,不同的RDD将保留下来
  2. 需求:创建两个RDD,求第一个RDD与第二个RDD的差集
(1)创建第一个RDD 
scala> val rdd = sc.parallelize(3 to 8) 
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[70] at parallelize at <console>:24 
(2)创建第二个RDD 
scala> val rdd1 = sc.parallelize(1 to 5) 
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[71] at parallelize at <console>:24 
(3)计算第一个RDD与第二个RDD的差集并打印 
scala> rdd.subtract(rdd1).collect() 
res27: Array[Int] = Array(8, 6, 7) 
2.3.2.3 intersection(otherDataset) 案例
  1. 作用:对源RDD和参数RDD求交集后返回一个新的RDD
  2. 需求:创建两个RDD,求两个RDD的交集
(1)创建第一个RDD
 scala> val rdd1 = sc.parallelize(1 to 7) 
 rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[26] at parallelize at <console>:24
(2)创建第二个RDD 
scala> val rdd2 = sc.parallelize(5 to 10) 
rdd2: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[27] at parallelize at <console>:24 
(3)计算两个RDD的交集 
scala> val rdd3 = rdd1.intersection(rdd2) 
rdd3: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[33] at intersection at <console>:28 
(4)打印计算结果 
scala> rdd3.collect()
2.3.3 Key-Value类型
2.3.3.1 partitionBy案例
  1. 作用:对pairRDD进行分区操作,如果原有的partionRDD和现有的partionRDD是一致的话就不进行分区, 否则会生成ShuffleRDD,即会产生shuffle过程。
  2. 需求:创建一个4个分区的RDD,对其重新分区
(1)创建一个RDD 
scala> val rdd = sc.parallelize(Array((1,"aaa"),(2,"bbb"),(3,"ccc"),(4,"ddd")),4)
rdd: org.apache.spark.rdd.RDD[(Int, String)] = ParallelCollectionRDD[44] at parallelize at <console>:24 
(2)查看RDD的分区数 
scala> rdd.partitions.size res24: Int = 4 
(3)对RDD重新分区 
scala> var rdd2 = rdd.partitionBy(new org.apache.spark.HashPartitioner(2)) 
rdd2: org.apache.spark.rdd.RDD[(Int, String)] = ShuffledRDD[45] at partitionBy at <console>:26 
(4)查看新RDD的分区数
 scala> rdd2.partitio
2.3.3.2 reduceByKey(func, [numTasks]) 案例
  1. 在一个(K,V)的RDD上调用,返回一个(K,V)的RDD,使用指定的reduce函数,将相同key的值聚合到一起,reduce任务的个数可以通过第二个可选的参数来设置。
  2. 需求:创建一个pairRDD,计算相同key对应值的相加结果
(1)创建一个pairRDD
 scala> val rdd = sc.parallelize(List(("female",1),("male",5),("female",5),("male",2))) 
 rdd: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[46] at parallelize at <console>:24 
(2)计算相同key对应值的相加结果 
scala> val reduce = rdd.reduceByKey((x,y) => x+y) 
reduce: org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[47] at reduceByKey at <console>:26
(3)打印结果 scala> reduce.collect() 
res29: Array[(String, Int)] = Array((female,6), (male,7)) 
2.3.3.3 groupByKey案例
  1. 作用:groupByKey也是对每个key进行操作,但只生成一个seq。
  2. 需求:创建一个pairRDD,将相同key对应值聚合到一个seq中,并计算相同key对应值的相加结果。
(1)创建一个pairRDD 
scala> val words = Array("one", "two", "two", "three", "three", "three") 
words: Array[String] = Array(one, two, two, three, three, three) 
scala> val wordPairsRDD = sc.parallelize(words).map(word => (word, 1)) 
wordPairsRDD: org.apache.spark.rdd.RDD[(String, Int)] = MapPartitionsRDD[4] at map at <console>:26 
(2)将相同key对应值聚合到一个Seq中 
scala> val group = wordPairsRDD.groupByKey() 
group: org.apache.spark.rdd.RDD[(String, Iterable[Int])] = ShuffledRDD[5] at groupByKey at <console>:28 
(3)打印结果 
scala> group.collect() 
res1: Array[(String, Iterable[Int])] = Array((two,CompactBuffer(1, 1)), (one,CompactBuffer(1)), (three,CompactBuffer(1, 1, 1))) 
(4)计算相同key对应值的相加结果 
scala> group.map(t => (t._1, t._2.sum)) 
res2: org.apache.spark.rdd.RDD[(String, Int)] = MapPartitionsRDD[6] at map at <console>:31 
(5)打印结果 
scala> res2.collect() res3: Array[(String, Int)] = Array((two,2), (one,1), (three,3)) 
2.3.3.4 reduceByKey和groupByKey的区别
  1. reduceByKey:按照key进行聚合,在shuffle之前有combine(预聚合)操作,返回结果是RDD[k,v]。
  2. groupByKey:按照key进行分组,直接进行shuffle。
  3. 开发指导:reduceByKey比groupByKey,建议使用。但是需要注意是否会影响业务逻辑。
2.3.3.5 aggregateByKey案例

参数:(zeroValue:U,[partitioner: Partitioner]) (seqOp: (U, V) => U,combOp: (U, U) => U)

  1. 作用:在kv对的RDD中,,按key将value进行分组合并,合并时,将每个value和初始值作为seq函数的参数,进行计算,返回的结果作为一个新的kv对,然后再将结果按照key进行合并,最后将每个分组的value传递给combine函数进行计算(先将前两个value进行计算,将返回结果和下一个value传给combine函数,以此类推),将key与计算结果作为一个新的kv对输出。
  2. 参数描述: (1)zeroValue:给每一个分区中的每一个key一个初始值; (2)seqOp:函数用于在每一个分区中用初始值逐步迭代value; (3)combOp:函数用于合并每个分区中的结果。
  3. 需求:创建一个pairRDD,取出每个分区相同key对应值的最大值,然后相加
  4. 需求分析: 取出每个分区相同key对应值的最大值,然后相加
    在这里插入图片描述
(1)创建一个pairRDD
 scala> val rdd = sc.parallelize(List(("a",3),("a",2),("c",4),("b",3),("c",6),("c",8)),2) 
 rdd: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[0] at parallelize at <console>:24 
(2)取出每个分区相同key对应值的最大值,然后相加 
scala> val agg = rdd.aggregateByKey(0)(math.max(_,_),_+_) 
agg: org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[1] at aggregateByKey at <console>:26 
(3)打印结果 
scala> agg.collect() 
res0: Array[(String, Int)] = Array((b,3), (a,3), (c,12)) 
2.3.3.6 foldByKey案例

参数:(zeroValue: V)(func: (V, V) => V): RDD[(K, V)]

  1. 作用:aggregateByKey的简化操作,seqop和combop相同
  2. 需求:创建一个pairRDD,计算相同key对应值的相加结果
(1)创建一个pairRDD 
scala> val rdd = sc.parallelize(List((1,3),(1,2),(1,4),(2,3),(3,6),(3,8)),3)
 rdd: org.apache.spark.rdd.RDD[(Int, Int)] = ParallelCollectionRDD[91] at parallelize at <console>:24 
(2)计算相同key对应值的相加结果 
scala> val agg = rdd.foldByKey(0)(_+_) agg: org.apache.spark.rdd.RDD[(Int, Int)] = ShuffledRDD[92] at foldByKey at <console>:26 
(3)打印结果 
scala> agg.collect() res61: Array[(Int, Int)] = Array((3,14), (1,9), (2,3))

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值