Spark键值对Pair RDD操作

1、键值对pair RDD产生的背景

pair RDD提供了一些新的操作接口,比如统计每个产品的评论,将数据中键相同的分为一组,将两个不同的RDD进行分组合并等。我们通常从一个RDD中提取某些字段如事件时间、用户ID,并使用这些字段作为pair RDD操作中的键。

2、创建pair RDD

(1)读取键值对格式数据时直接返回其键值对数据组成的pair RDD。
(2)使用map()函数将普通的RDD转为pair RDD。

3、pair RDD的转化操作

(1)mapValues()
对pair RDD中的每个值应用一个函数而不改变键,功能类似于map{case (x,y):(x,func(y))}

(2)reduceByKey()
与reduce相似,接收一个函数,为数据集中每个键进行并行的归约操作,每个归约操作会将键相同的值合并起来。返回一个由各键和对应键归约出来的结果值组成的新RDD。
例如:在Scala中使用reduceBykey()和mapValues()计算每个键对应的平均值
rdd.mapValues(x =>(x,1)).reduceByKey((x,y)=>(x._1 + y._1,x._2 + y._2))
(注:此处x=>(x,1)是为了构造pair RDD,reduceByKey(x,y)中的x,y指的是键相同的两个值)

(3)combineByKey()
基于键进行聚合的函数,与aggregate()一样,可以返回与输入数据类型不同的返回值。该操作的处理数据时会遍历分区中的所有元素,因此每个元素要么还没有遇到,要么就和之前的某个元素的键相同。

对于每个分区中的新元素,combineByKey()会使用createCombiner()函数来创建那个键对应的累加器的初始值。对于当前分区中已经遇到的键,它会使用mergeValue()方法将该键的累加器对应的当前值与这个新的值进行合并。

由于每个分区都是独立处理的,因此对于同一个键可以有多个累加器。如果有两个或者更多的分区都有对应同一个键的累加器,就需要使用用户提供的mergeCombiners()方法将各个分区的结果进行合并。

例如:在Scala中使用combinerByKey()求每个键对应的平均值
val result = lines.combineByKey(
(v) => (v,1),
(com:(Int,Int),v) => (com._1 + v,com._2 + 1),
(com1:(Int,Int),com2:(Int,Int)) => (com1._1 + com2._1,com1._2 + com2._2)
).map{case (key,value) => (key,value._1 / value.2.toFloat)}
result.collectAsMap().map(println(
))

(4)flatMapValues(func)
对pair RDD中的每个值应用一个返回迭代器的函数,然后对返回的每个元素都生成一个对应原键值对记录,通常用于符号化。
例如:集合{(1,3),(3,5)}
rdd.flatMapValues(x => (x to 6))
结果:{(1,3),(1,4),(1,5),(1,6),(3,5),(3,6)}

(5)groupByKey()
对单个RDD的数据进行分组,如果数据已经以预期的方式提取了键,此函数就会使用RDD中的键来对数据进行分组,对于一个由类型K的键和类型V的值组成的RDD,所得到的结果RDD类型会是[K,Iterable[V]]。
对于未成对的数据,它可以接收一个函数,对源RDD中的每个元素使用该函数,将返回结果作为键再进行分组。

(6)cogroup()
对多个共享同一个键的RDD进行分组,对两个键均为K而值类型分别为U和V的RDD进行cogroup()时,得到的结果RDD类型为[(K,(Iterable[U],Iterable[V]))],该函数提供了为多个RDD进行分组的方法。

(7)join()
内连接,只有在两个pair RDD中都存在的键才会输出,当一个输入对应的某个键有多个值时,生成的pair RDD会包含来自两个输入RDD的每一组相对应的记录。

(8)leftOuterJoin()
左外连接,使用此方法产生的pair RDD中,源RDD的每一个键都有对应的记录,每个键相应的值是由一个源RDD中的值与一个包含第二个RDD的值的Option对象的二元组。
(注:可以调用isPresent()来看值是否存在,如果数据存在,则可以调用get()来获得其中包含的对象实例)

(9)rightOuterJoin()
右外连接,与leftOuterJoin()几乎一样,只不过预期结果中的键必须出现在第二个RDD中,而二元组中的可缺失的部分则来自于源RDD而非第二个RDD。

(10)sortByKey()
默认是升序排序,若需要按不同的排序依据进行排序,可以提供自定义的比较函数。

(11)keys()
返回一个仅包含键的RDD。

(12)values()
返回一个仅包含值的RDD。

4、pair RDD的行动操作

(1)countByKey()
对每个键对应的元素分别计数。

(2)collectAsMap()
将结果以映射表的形式返回。

(3)lookup(key)
返回给定键对应的所有值。

5、并行度调优

每个RDD都有固定数目的分区,分区数决定了在RDD上执行操作时的并行度。有执行聚合或分组操作时,可以要求Spark使用给定的分区数。
Spark始终尝试根据集群的大小推断出一个有意义的默认值,但有时候需对并行度进行调优来获取更好的性能表现,有两种方法可指定并行度。

(1)在函数操作时指定分区数
本文讨论的大多函数都能接收第二个参数来指定分组或聚合结果的RDD的分区数,如,rdd.reduceByKey((x,y) => x + y,10) 10为并行度。

(2)在函数操作外指定分区数
若在分组操作和聚合操作之外的操作中改变RDD的分区,可以使用repartition()函数,它会把数据通过网络进行混洗,并创建出新的分区集合,但是代价相对较大。此外Spark中有一个优化版的分区函数coalesce()。
可以使用Java或Scala中的rdd.partitions.size或Python中的rdd.getNumpartitions查看RDD的分区数,并确保调用coalesce()时将RDD合并到比现在分区数更少的分区中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值