键值对RDD是Spark中许多操作所需要的常见数据类型。键值对RDD通常用来进行聚合运算,我们一般要先通过一些初始ETL(抽取,转化,装载)操作来将数据转化为键值对形式。
Spark为包含键值对类型的RDD提供了一些专有的操作,这些RDD被称为PairRDD。
创建 pair RDD
很多存储键值对的数据格式会在读取时直接返回其由键值对数据组成的pair RDD,当需要把一个普通的RDD转化为pair RDD时,可以调用map()来实现
使用第一个单词作为键来创建pair RDD
val pairs = line.map(x => (x.split(" ")(0),x))
pair RDD 转化操作
//假设有一个键值对集合{(1,2),(3,4),(3,6)}
//reduceByKey(func) 合并具有相同建的值
rdd.reduceByKey((x,y) => x+y) //{(1,2),(3,10)}
//groupByKey() 对具有相同的键值进行分组
rdd.groupByKey() //{(1,[2]),(3,[4,6])}
//mapValues 对pair RDD 中的每个值应用一个函数而不改变键
rdd.mapValues(x => x+1) //{(1,3),(3,5),(3,7)}
//flatMapValues(func) 对pair RDD中的每个值应用一个返回迭代器的函数,然后对返回的每个元素都生成一个对应原键的键值对记录,通常用于符号化。
rdd.flatMapValues(x => (x to 5)) //{(1,2),(1,3),(1,4),(1,5),(3,4),(3,5)}
//keys 返回一个仅包含键的RDD
rdd.keys //注意此处不需要使用括号{1,3,3}
//values 返回一个仅包含值得RDD
rdd.values //注意此处不需要使用括号{2,4,6}
//sortByKey 返回一个根据键进行排序的RDD
rdd.sortByKey() //{(1,2),(3,4),(3,6)}
针对两个pair RDD的转化操作
//假设有键值对集合(rdd = {(1,2),(3,4),(3,6)} ,others = {(3,9)}
//subtractByKey 删掉RDD中键与other RDD中的键相同的元素
rdd.subtractByKey(others) //{(1,2)}
//join 对两个RDD进行内连接
rdd.join(others) //{(3,(4,9)),(3,(6,9))}
//rightOuterJoin 对两个RDD进行连接操作,确保第一个RDD的键必须存在(右外连接)
rdd.rightOuterJoin(others) //{(3,(Some(4),9)),(3,(Some(6),9))}
//leftOuterJoin 对两个RDD进行连接操作,确保第二个RDD的键必须存在(左外连接)
rdd.rightOuterJoin(others) //{(1,(2,None)),(3,(4,Some(9),(3,(6,Some(9)))}
//cogroup 将两个RDD中拥有相同的键的数据分组到一起
rdd.cogroup(others) //{(1,([2],[])),(3,([4,6],[9]))}
pair RDD也还是RDD,所以pair RDD也支持RDD所支持的所有函数
例:筛选掉长度超过20个的字符的行
pairs.filter{case(key,value)=>value.length < 20}
聚合操作
当数据集以键值对的形式组织的时候,聚合具有相同键的元素进行一些统计是很常见的操作。
例:在Scala中使用reduceByKey()和mapValues()计算每个键值对对应的平均值。
rdd.mapValues(x=>(x,1)).reduceByKey((x,y) => (x._1 + y._1,x._2 + y._2))
求键值时的数据流如下图所示:
例:用Scala实现单词计数
val input = sc.textFile("s3://...")
val words = input.flatMap(x => x.split(""))
val result = words.map(x => (x,1)).reduceByKey((x,y) => x+y)
Pair RDD的行动操作
和转化操作一样,所有基础RDD支持的传统行动操作也都在pairRDD上可用,pair RDD提供了一些额外的行动操作,可以让我们充分利用数据的键值对特性。
//假设有键值对集合{(1,2),(3,4),(3,6)}
//countByKey 对每个键对应的元素分别计数
rdd.countByKey() //{(1,1),(3,2)}
//collectAsMap 将结果以映射表的形式返回,以便查询
rdd.collectAsMap() //map{(1,2),(3,6)}
//lookup 返回给定键对应的所有值
rdd.lookup(3) //[4,6]
本文讲述了RDD键值对的一些基本操作函数,后续会继续更新多种进阶操作。