按颜色区分转换:
这个 RDD 在第一次分析中已经分析过。简单复述一下:
在分区中采样的RDD
笛卡尔积,是两个 RDD 每个数据都进行一次关联。下文中两个 RDD 的关联中,两个 RDD 分别称为 rdd1、rdd2。
洗牌型转换,是多个 RDD 关联的的转换。
多个源 RDD 依据 key 关联,key 相同的合并,形成最终的目标 RDD。
同样是多个源 RDD 依据 key 关联,key 相同的做排序或聚合运算,形成最终的目标 RDD。
除了这五个成员以外,还有另外几个重要的成员:序列化器、key 排序器、聚合器、map 端合并器,他们都将用于洗牌
在 RDD.scala中,几乎每一个转换和操作函数都会有一个withScope,例如:
def map[U: ClassTag](f: T => U): RDD[U] = withScope {
val cleanF = sc.clean(f)
new MapPartitionsRDD[U, T](this, (context, pid, iter) => iter.map(cleanF))
}
def flatMap[U: ClassTag](f: T => TraversableOnce[U]): RDD[U] = withScope {
val cleanF = sc.clean(f)
new MapPartitionsRDD[U, T](this, (context, pid, iter) => iter.flatMap(cleanF))
}
withScope是一个函数,调用了RDDOperationScope.withScope方法:
private[spark] def withScope[U](body: => U): U = RDDOperationScope.withScope[U](sc)(body)
withScope就像是一个 AOP(面向切面编程),嵌入到所有RDD 的转换和操作的函数中,RDDOperationScope会把调用栈记录下来,用于绘制Spark UI的 DAG(有向无环图,可以理解为 Spark 的执行计划)。
我们用下面的代码简单演示一下 Scala 用函数做 AOP:
object Day1 {
def main(args: Array[String]) = {
Range(1,5).foreach(twice)
println()
Array("China", "Beijing", "HelloWorld").foreach(length)
}
def twice(i: Int): Int = aopPrint {
i * 2
}
def length(s: String): Int = aopPrint {
s.length
}
def aopPrint[U](i: => U): U = {
print(i + " ")
i
}
}
aopPrint的 入参是“一个返回类型为U的函数”。这段程序中aopPrint就是一个模拟的切面,作用是把所有的函数返回值打印出来。结果是:
2 4 6 8
5 7 10
从代码上看,aopPrint并没有降低代码的可读性。读者依然能很清楚地读懂twice和length函数。打印返回结果这个流程是独立于函数之外的切面。