Spark 为包含键值对类型的RDD提供了一些专有的擦我做。这些RDD被称为pair RDD。
在 Spark 中有很多种创建 pair RDD 的方式。第 5 章会讲到,很多存储键值对的数据格式会在读取时直接返回由其键值对数据组成的 pair RDD。此外,当需要把一个普通的 RDD 转为 pair RDD 时,可以调用 map() 函数来实现,传递的函数需要返回键值对。
在 Scala 中使用第一个单词作为键创建出一个 pair RDD
val pairs = lines.map(x => (x.split(" ")(0), x))
Pair RDD的转化操作
由于 pair RDD 中包含二元组,所以需要传递的函数应
当操作二元组而不是独立的元素。
针对一个RDD的转化操作
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])} |
combineByKey() | 使用不同的返回类型合并具有相同键的值 | ||
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)} |
针对量RDD的转化操作
rdd = {(1,2),(3,4),(3,6)},other = {(3,9)}
函数名 | 目的 | 示例 | 结果 |
---|---|---|---|
subtractByKey | 删掉RDD中键与other RDD中的键相同的元素 | rdd.subtractByKey(other) | {(3,9)} |
join | 对两个RDD进行内连接 | rdd.join(other) | {(3,(4,9),(3,(6,9)))} |
ightOuterJoin | 对两个 RDD 进行连接操作,确保第一个 RDD的键必须存在(右外连接) | rdd.rightOuterJoin(other) | {(3,(Some(4),9)),(3,(Some(6),9))} |
leftOuterJoin | 对两个 RDD 进行连接操作,确保第二个 RDD 的键必须存在(左外连接 | rdd.leftOuterJoin(other) | {(1,(2,None)), (3,(4,Some(9))), (3,(6,Some(9)))} |
cogroup | 将两个 RDD 中拥有相同键的数据分组到一起 | rdd.cogroup(other) | {(1,([2],[])), (3,([4, 6],[9]))} |
Pair RDD 也还是 RDD ,因此同样支持RDD所支持的函数
用 Scala 对第二个元素进行筛选
pairs.filter{case (key, value) => value.length < 20}
聚合操作
当数据集以键值对形式组织的时候,聚合具有相同键的元素进行一些统计是很常见的操作。
聚合操作返回RDD,是转化操作。
1、reduceByKey()
与 reduce() 相当类似;它们都接收一个函数,并使用该函数对值进行合并。
reduceByKey() 会为数据集中的每个键进行并行的归约操作,每个归约操作会将键相同的值合并起来。
2、foldByKey()
与 fold() 相当类似;
它们都使用一个与 RDD 和合并函数中的数据类型相同的零值作为初始值。
与 fold() 一样, foldByKey() 操作所使用的合并函数对零值与另一个元素进行合并,结果仍为该元素。
用 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)
3、combineByKey()
combineByKey()是最为常用的基于键进行聚合的函数。大多数基于键聚合的函数都是用它实现的。和 aggregate() 一样, combineByKey() 可以让用户返回与输入数据的类型不同的返回值。
由于combineByKey() 会遍历分区中的所有元素。键值对操作如果这是一个新的元素,会使用一个叫作createCombiner()的函数来创建那个键对应的累加器的初始值。这一过程会在每个分区中第一次出现各个键时发生,而不是在整个RDD中第一次出现一个键时发生。
如果这是一个在处理当前分区之前已经遇到的键,它会使用 mergeValue()方法将该键的累加器对应的当前值与这个新的值进行合并
并行度调忧
每个RDD都有固定数目的分区,分区数决定了在RDD上执行操作时的并行度。Spark始终尝试根据集群的大小推断出一个有意义的默认值,但是有时候你可能要对并行度进行调优来获取更好的性能表现。
本章讨论的大多数操作符都能接收第二个参数, 这个参数用来指定分组结果或聚合结果的
RDD 的分区数
自定义并行度
val data = Seq(("a", 3), ("b", 4), ("a", 1))
sc.parallelize(data).reduceByKey((x, y) => x + y) // 默认并行度
sc.parallelize(data).reduceByKey((x, y) => x + y,10) // 自定义并行度
数据分组
1、groupByKey()
groupByKey()使用RDD中的键来对数据进行分组。对于一个由类型 K 的键和类型V的值组成的RDD,所得到的结果 RDD 类型会是[K, Iterable[V]]。
2、groupBy()
groupBy()可以用于未成对的数据上,也可以根据除键相同以外的条件进行分组。
groupBy()可以接受一个函数,对每个元素使用该函数,将返回结果作为键再分组。
3、cogroup()
对多个共享同一个键的RDD进行分组。
对两个键的类型均为 K 而值的类型分别为 V 和 W的RDD进行cogroup()时,得到的结果RDD类型为[(K,(Iterable[V],Iterable[W]))]。
如果其中的一个RDD对于另一个RDD中存在的某个键没有对应的记录,那么对应的迭代器则为空。
连接
连接方式:1、右外连接;2、左外连接;3、交叉连接;4、内连接。
1、join()
join()操作表示内连接。只有两个pair RDD都存在的键才会有输出。
当一个输入对应的某个键值有多个值时,生成的pair RDD会包含来自两个输入RDD的两两组合的记录
在scala shell 中进行内连接
storeAddress = {
(Store("Ritual"), "1026 Valencia St"), (Store("Philz"), "748 Van Ness Ave"),
(Store("Philz"), "3101 24th St"), (Store("Starbucks"), "Seattle")}
storeRating = {
(Store("Ritual"), 4.9), (Store("Philz"), 4.8))}
storeAddress.join(storeRating) == {
(Store("Ritual"), ("1026 Valencia St", 4.9)),
(Store("Philz"), ("748 Van Ness Ave", 4.8)),
(Store("Philz"), ("3101 24th St", 4.8))}
2、letfOuterJoin(other) / rightOuterJoin(other)
允许区中一个pair RDD缺失键值,left允许第二个RDD缺失,right允许源RDD缺失。
storeAddress.leftOuterJoin(storeRating) ==
{(Store("Ritual"),("1026 Valencia St",Some(4.9))),
(Store("Starbucks"),("Seattle",None)),
(Store("Philz"),("748 Van Ness Ave",Some(4.8))),
(Store("Philz"),("3101 24th St",Some(4.8)))}
storeAddress.rightOuterJoin(storeRating) ==
{(Store("Ritual"),(Some("1026 Valencia St"),4.9)),
(Store("Philz"),(Some("748 Van Ness Ave"),4.8)),
(Store("Philz"), (Some("3101 24th St"),4.8))}
数据排序
1、sortByKey()
接收参数:ascending,默认为true。
支持自定义函数
val input: RDD[(Int, Venue)] = ...
implicit val sortIntegersByString = new Ordering[Int] {
override def compare(a: Int, b: Int) = a.toString.compare(b.toString)
}
rdd.sortByKey()
Pair RDD的行动操作
所有基础RDD支持的行动操作,都可以在pair RDD上使用。
Pair RDD的行动操作
函数 | 功能 | 示例 | 结果 |
---|---|---|---|
countByKey() | 对每个键对应的元素分别计数 | rdd.countByKey() | {(1,1),(3,2)} |
collectAsMap() | 将结果以映射表的形式返回,以便 | 查询 | rdd.collectAsMap() |
lookup(key) | 返回给定键对应的所有值 | rdd.lookup(3) | [4.6] |