RDD分两类:actions 与 transformation。transformation中有些算子理解起来比较费解,先理解action算子再理解transformation算子会容易些。
ACTION
collect
count
first
返回RDD的第一个成员,等价于take(1)
take
返回RDD前n个成员
takeSample
语法:
def takeSample( withReplacement: Boolean, num: Int, seed: Long = Utils.random.nextLong): Array[T]
说明: 和 sample 用法相同,只不第二个参数换成了个数。返回也不是RDD,而是collect
takeOrdered
语法:
def takeOrdered(num: Int): JList[T]
def takeOrdered(num: Int, comp: Comparator[T]): JList[T]
说明: 用于从RDD中,按照默认(升序)或指定排序规则,返回前num个元素。
saveAsTextFile
saveAsSequenceFile
以SequenceFile格式保存,成员类型必须实现Writeable接口或可以被隐式转换为Writable类型
saveAsObjectFile
将RDD中的元素序列化成对象,存储到文件中
countByKey
仅适用于(K, V)类型,对key计数,返回(K, Int)
foreach
类似map,但无返回值,常用于更新计数器或输出数据至外部存储系统。
reduce
语法:
def reduce(f: (T, T) => T): T
说明: Rdd<T> resRdd = rdd<T>.reduce(func: (T,T) => T)
对rdd内所有元素进行合并计算,方法是从左至右两两计算,最后只得到一个值
aggregate
语法:
def aggregate[U: ClassTag](zeroValue: U)(seqOp: (U, T) => U, combOp: (U, U) => U): U
说明: 即每个分区内以 zeroValue 初始值进行 reduce ,再对 各分区结果 以及 zeroValue 进行 reduce 。 与 reduce 的区别是, aggregate 有对分区内的操作定义 和 对分区间的操作定义;还有初始值定义 ;reduce 仅有一个统一的对所有元素的操作定义
Rdd<U> resRdd = rdd<T>.aggregate(zeroValue: U)( seqOp: (U, T) => U, combOp: (U, U) => U )
举例:
rdd 有两个分区
第一个分区中包含5,4,3,2,1
第二个分区中包含10,9,8,7,6
scala> rdd.aggregate(1)(
| {(x : Int,y : Int) => x + y},
| {(a : Int,b : Int) => a + b}
| )
res17: Int = 58
分析: zeroValue 为 1 (即第一个括号内值)
先在每个分区中迭代执行 (x : Int,y : Int) => x + y 并且使用zeroValue的值1
即:
part_0中 zeroValue+5+4+3+2+1 = 1+5+4+3+2+1 = 16
part_1中 zeroValue+10+9+8+7+6 = 1+10+9+8+7+6 = 41
再将两个分区的结果合并(a : Int,b : Int) => a + b ,并且使用zeroValue的值1
即:
zeroValue+part_0+part_1 = 1 + 16 + 41 = 58
Transformation
mapPartitions
语法:
def mapPartitions[U: ClassTag]( f: Iterator[T] => Iterator[U], preservesPartitioning: Boolean = false): RDD[U]
说明: 参数 preservesPartitioning 表示是否保留父RDD的partitioner分区信息。 如果在映射的过程中需要频繁创建额外的对象,使用 mapPartitions 要比map高效。
示例:
Rdd<Iterator<T>> resRdd = rdd<T>.mapPartitions(func: Iterator[T] => Iterator[U], preservesPartitioning: Boolean = false)
// 输入为一个分区的所有数据,输出 Iterator
mapPartitionsWithIndex
语法:
def mapPartitionsWithIndex[U](f: (Int, Iterator[T]) => Iterator[U], preservesPartitioning: Boolean = false)(implicit arg0: ClassTag[U]): RDD[U]
说明: 同mapPartitions,不过提供了两个参数,第一个参数为分区的索引。
示例: Rdd<Iterator<T>> resRdd = rdd<T>.mapPartitions(func: (Int, Iterator[T]) => Iterator[U], preservesPartitioning: Boolean = false)
sample
语法:
def sample( withReplacement: Boolean, fraction: Double, seed: Long = Utils.random.nextLong): RDD[T]
说明: withReplacement 为true时表示抽样之后还放回,可以被多次抽样,false表示不放回; fraction 表示抽样比例;
Rdd resRdd = rdd.sample(false,0.8)
union
语法: def union(other: RDD[T]): RDD[T]
说明: 简单合并两个RDD,不去重,要求两个RDD中的元素类型一致
Rdd<T> resRdd = rdd<T>.union(rdd2<T>)
举例:
data1RDD = sc.parallelize(Arrays.asList("张三", "李四"));
data2RDD = sc.parallelize(Arrays.asList("tom", "gim"));
data1RDD.union(data2RDD) 结果为元素分别为"张三", "李四","tom", "gim"的rdd
intersection
语法: def intersection(other: RDD[T]): RDD[T]
说明: 返回两个RDD的交集
groupby
语法:
def groupBy[K](f: T => K)(implicit kt: ClassTag[K]): RDD[(K, Iterable[T])] // K 即组号
说明: 将元素分组, call 返回值为组号
Rdd<K, Iterable[T]> resRdd = rdd<T>.groupBy(func: T => K)
groupByKey
语法: def groupByKey(): RDD[(K, Iterable[V])]
说明: rdd
Rdd<K, Iterable[V]> resRdd = rdd<K,V>.groupByKey()
reduceByKey
语法: def reduceByKey(func: (V, V) => V): RDD[(K, V)]
说明:
Rdd<K, V> resRdd = rdd<K, V>.reduceByKey(func: (V, V) => V) //从左向右合并
aggregateByKey
语法: def aggregateByKey[U: ClassTag](zeroValue: U)(seqOp: (U, V) => U, combOp: (U, U) => U): RDD[(K, U)]
说明: 对 key 相同的值进行合并,合并方法是 先各分区内合并,再分区间合并。合并都有 zeroValue 参与
zeroValue :表示在每个分区中第一次拿到key值时,用于创建一个返回类型的函数,这个函数最终会被包装成先生成一个返回类型,然后通过调用seqOp函数,把第一个key对应的value添加到这个类型U的变量中。
seqOp :这个用于把迭代分区中key对应的值添加到zeroValue创建的U类型实例中。
combOp :这个用于合并每个分区中聚合过来的两个U类型的值。
举例:
rdd 有两个分区 ,rdd类型为 <Int, Int>
分区一数据
(1,3) (1,2)
分区二数据
(1,4) (2,3)
代码:
def seq(a: String, b: Int): String = { // a 为分区输出类型,b 为分区内元素类型
println("seq: " + a + "\t " + b)
a + b
}
def comb(a: String, b: String): String = { // a 与 b 都是分区输出类型
println("comb: " + a + "\t " + b)
a + b
}
val aggregateByKeyRDD: RDD[(Int, String)] = rdd.aggregateByKey("100")(seq, comb)
**说明**
//分区一相同key的数据进行合并。下面形式 seq: 输入参数
seq: 100 3 //(1,3)开始和中立值进行合并 合并结果为 1003
seq: 1003 2 //因为key都是1,前次合并结果与(1,2)再次合并 结果为 10032
//分区二相同key的数据进行合并
seq: 100 4 //(1,4) 开始和中立值进行合并 1004
seq: 100 3 //(2,3) 开始和中立值进行合并 1003
将两个分区的结果进行合并
//key为2的,只在一个分区存在,不需要合并 (2,1003)
(2,1003)
//key为1的, 在两个分区存在,并且数据类型一致,合并
comb: 10032 1004
(1,100321004)
最终结果是 :(1,100321004),(2,1003)
sortBy
语法: def sortBy[K]( f: (T) => K, ascending: Boolean = true, numPartitions: Int = this.partitions.length): RDD[T]
说明: 按f函数输出结果排序, ascending true为升序, rdd分区数改为 numPartitions
sortByKey
语法: def sortByKey(ascending: Boolean = true, numPartitions: Int = self.partitions.length): RDD[(K, V)]
说明: 按key值排序, (3, 3) (2, 2) (1, 4) (2, 3) 输出: (3,3) (2,2) (2,3) (1,4)
join
语法: def join[W](other: RDD[(K, W)]): RDD[(K, (V, W))]
说明: join相当于SQL中的内关联join,只返回两个RDD根据K可以关联上的结果
Rdd<(K, (V, W))> resRdd = rdd<K, V>.join(rdd2<K, W>)
举例:
sc.parallelize(List((1, "苹果"), (2, "梨"), (3, "香蕉"), (4, "石榴")))
.join(sc.parallelize(List((1, 7), (2, 3), (4, 3), (5, 9))))
.foreach(println)
结果:
(4,(石榴,3))
(1,(苹果,7))
(2,(梨,3))
cogroup
语法:
def cogroup[W1, W2, W3](other1: RDD[(K, W1)],
other2: RDD[(K, W2)],
other3: RDD[(K, W3)],
partitioner: Partitioner)
: RDD[(K, (Iterable[V], Iterable[W1], Iterable[W2], Iterable[W3]))] // 此为与三个其它rdd cogroup的,还有参数为一个rdd、两个rdd的 cogroup 函数
说明: cogroup相当于SQL中的全外关联full outer join,返回左右RDD中的记录,关联不上的为空。
与 join 的区别是:1 join只能一次关联一个rdd;2 join是内关联(不返回关联不上的),cogroup 是外关联(关联不上返回空)
举例:
var rdd1 = sc.makeRDD(Array(("A","1"),("B","2"),("C","3")),2)
var rdd2 = sc.makeRDD(Array(("A","a"),("C","c"),("D","d")),2)
var rdd3 = rdd1.cogroup(rdd2)
rdd3.collect
结果: Array[(String, (Iterable[String], Iterable[String]))] = Array(
(B,(CompactBuffer(2),CompactBuffer())),
(D,(CompactBuffer(),CompactBuffer(d))),
(A,(CompactBuffer(1),CompactBuffer(a))),
(C,(CompactBuffer(3),CompactBuffer(c)))
)
cartesian
语法: def cartesian[U: ClassTag](other: RDD[U]): RDD[(T, U)]
说明: 两个RDD进行笛卡尔积合并
Rdd<(T, U)> resRdd = rdd<T>.cartesian(rdd2<U>)
举例:
var rdd1 = sc.makeRDD(Array("张三", "李四", "王五"))
var rdd2 = sc.makeRDD(Array(60, 70, 80))
namesRDD.cartesian(scoreRDD).foreach(println)
结果:
张三 60
张三 70
张三 80
李四 60
李四 70
李四 80
王五 60
王五 70
王五 80
pipe
语法:
def pipe(command: String): RDD[String]
def pipe(command: String, env: Map[String, String]): RDD[String]
说明: 执行cmd命令,创建RDD
举例:
val data = List("hi", "hello", "how", "are", "you")
sc.makeRDD(data)
.pipe("/Users/zhangws/echo.sh")
.collect()
.foreach(println)
echo.sh 内容为:
(
#!/bin/bash
echo "Running shell script"
RESULT=""
while read LINE; do
RESULT=${RESULT}" "${LINE}
done
echo ${RESULT} > /Users/zhangws/out123.txt
)
结果:
out123.txt
hi hello how are you
输出
Running shell script
coalesce
语法: def coalesce(numPartitions: Int, shuffle: Boolean = false)(implicit ord: Ordering[T] = null): RDD[T]
说明: 用于将RDD进行重分区,使用HashPartitioner。且该RDD的分区个数等于numPartitions个数。如果shuffle设置为true,则会进行shuffle。
repartition
repartitionAndSortWithinPartitions
语法: def repartitionAndSortWithinPartitions(partitioner: Partitioner): RDD[(K, V)]
说明: 根据给定的Partitioner重新分区,并且每个分区内根据comp实现排序。