Spark核心-键值对RDD

概述

键值对RDD一般通过初始化操作将数据转换成K-V形式,使用可控的分区方式把常被一起访问的数据放在同一个节点上,可以大大减少应用的通信开销。

Pair RDD

当从内存数据集创建pairRDD时,需要调用sparkContext.parallelizePairs
如{(1,2), (3,4), (3,6)}

函数描述注意示例
reduceByKey合并具有相同key的值{(1,2), (3,10)}
groupByKey对相同key的值分组{(1,[2]), (3,[4,6])}
combineByKey使用不同的返回类型并具有相同key的值
mapValues对pairRDD的每个value应用函数而不改变key{(1,3), (3,5), (3,7)}
flatMapValues对pairRDD每个value应用函数{(1,2), (1,3), (1,4), (1,5), (3,4), (3,5)}
keys返回仅包含key的RDD
values返回仅包含value的RDD
sortByKey返回一个根据key排序的RDD
subtractByKey删除RDD中key与另一个RDD中key相同的元素
join内连接
rightOuterJoin右连接
leftOuterJoin左连接
cogroup将两个RDD 中拥有相同键的数据分组到一起

注意:
调用reduceByKey和foldByKey会在计算key的全局结果之前先自动在每台机器上进行本地合并。

并行化调优

每个RDD都有固定数目的分区,分区数决定了RDD在执行时的并行度。
在执行聚合和分组操作时,可以要求Spark使用给定的分区数,虽然Spark会根据集群的大小推测一个有用的默认值,但是可以修改的。
如果希望在分组操作和聚合操作之外也能改变分区,可以repartition,会将数据通过网络混洗,创建新的分区集合,优化版的repartition是coalesce,可以rdd.getNumPartitions 查看RDD 的分区数,确保将RDD合并到比现在的RDD分区更少的分区中。
在分布式程序中,通信的代价是很大的,Spark通过控制RDD分区的方式来减少通信开销,只有当RDD多次在诸如join这种基于键操作使用时,分区才会有帮助。
Spark所有的pairRDD都可以分区,系统会根据一个针对key的函数对元素分区,Spark可以保证同一组的key出现在同一个节点上。

val sc = new SparkContext(...)
val userData = sc.sequenceFile[UserID, UserInfo]("hdfs://...").persist()
// 周期性调用函数来处理过去五分钟产生的事件日志
// 假设这是一个包含(UserID, LinkInfo)对的SequenceFile
def processNewLogs(logFileName: String) {
val events = sc.sequenceFile[UserID, LinkInfo](logFileName)
val joined = userData.join(events)// RDD of (UserID, (UserInfo, LinkInfo)) pairs
val offTopicVisits = joined.filter {
case (userId, (userInfo, linkInfo)) => // Expand the tuple into its components
!userInfo.topics.contains(linkInfo.topic)
}.count()
println("Number of visits to non-subscribed topics: " + offTopicVisits)
}

如上代码:
每次调用processNewLogs时都会join,默认情况下,join会将两个RDD的所有key的hash算出来,然后将hash相同的记录通过网络传到同一台机器上,然后在那台机器上对所有key相同的记录进行join,每次在join时都得对userData进行hash计算和跨节点数据混洗,虽然这些数据都不会变化。

val userData = sc.sequenceFile[UserID, UserInfo]("hdfs://...")
.partitionBy(new HashPartitioner(100)) // 构造100个分区
.persist()

可以在userData调用partitionBy指定分区,Spark就指定该RDD是按key的hash来分区的,这样在调用join时Spark只会对events数据进行混洗,网络传输数据就大大减少了。
事实上,很多Spark操作会自动为结果RDD设置已知的分区方式,如sortByKey和groupByKey会分别生成范围分区的RDD和哈希分区的RDD,而另一方面map会导致新RDD丢失父RDD的分区信息。

从分区中获益

Spark的很多操作都引入了将数据根据跨节点进行混洗的过程,所有这些操作都可以从分区中获益。如cogroup、groupWith、join、leftOuterJoin、rightOuterJoin、groupByKey、reduceByKey、combineByKey、lookup。
对于诸如cogroup() 和join() 这样的二元操作,预先进行数据分区会导致其中至少一个RDD不发生数据混洗,如果两个RDD 使用同样的分区方式,并且它们还缓存在同样的机器上(比如一个RDD 是通过mapValues() 从另一个RDD 中创建出来的,这两个RDD 就会拥有相同的键和分区方式),或者其中一个RDD 还没有被计算出来,那么跨节点的数据混洗就不会发生了。

影响分区的操作

当对一个哈希分区的key调用map时,由于map理论上会改变key,因此结果不会有固定的分区方式,可以使用mapValues和flatMaoValues替换,会保留原来的key。
这些操作都会保留上级分区信息:
cogroup()、groupWith()、join()、lef tOuterJoin()、rightOuterJoin()groupByKey()、reduceByKey()、combineByKey()partitionBy()、sort()、mapValues()(如果父RDD有分区方式的话)、flatMapValues()

Spark快速大数据分析

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值