import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkContext, SparkConf}
object Ex2_Computations {
/**
* 这个方法就是将信息按照树形模型打印,前面每一级加了一个' '
* @param r
* @param depth
* @tparam T
*/
private def showDep[T](r: RDD[T], depth: Int) : Unit = {
println("".padTo(depth, ' ') + "RDD id=" + r.id)
r.dependencies.foreach(dep => {
showDep(dep.rdd, depth + 1)
})
}
def showDep[T](r: RDD[T]) : Unit = {
showDep(r, 0)
}
def main(args: Array[String]) {
val conf = new SparkConf().setAppName("Ex2_Computations").setMaster("local[4]")
val sc = new SparkContext(conf)
/**
* 以下这部分和上一篇相同,就是把值乘100再加一
* toDebugString是按照debug模式打印RDD信息方便在不能断点的地方调试
*
*/
val numbers = sc.parallelize(1 to 10, 4)
val bigger = numbers.map(n => n * 100)
val biggerStill = bigger.map(n => n + 1)
println("以Debug string模式打印RDD 'biggerStill'")
println(biggerStill.toDebugString)
/**
* reduce操作比较简单,就是将整个集合的操作做类似如下操作
* int i = 0
* for(j=1;j<n;j++){
* i = i+j
* }
*就是将所有Seq中的元素按顺序执行一遍
*/
val s = biggerStill.reduce(_ + _)
println("sum = " + s)
/**
* 以下部分都是对相关信息的打印
* 这里面注意这几个id值,这几个id值是这个代码主要想讲述的东西
* IDs of the various RDDs
* numbers: id=0
* bigger: id=1
* biggerStill: id=2
* dependencies working back from RDD 'biggerStill'
* RDD id=2
* RDD id=1
* RDD id=0
* 这里面的意思是biggerStill依赖于bigger,而bigger依赖于numbers
*/
println("IDs of the various RDDs")
println("numbers: id=" + numbers.id)
println("bigger: id=" + bigger.id)
println("biggerStill: id=" + biggerStill.id)
println("dependencies working back from RDD 'biggerStill'")
showDep(biggerStill)
/**
* bigger ++ biggerStill
* 相当于两个RDD进行拼接其他与上面相同
* moreNumbers: id=3
* RDD id=3
* RDD id=1
* RDD id=0
* RDD id=2
* RDD id=1
* RDD id=0
*/
val moreNumbers = bigger ++ biggerStill
println("The RDD 'moreNumbers' has mroe complex dependencies")
println(moreNumbers.toDebugString)
println("moreNumbers: id=" + moreNumbers.id)
showDep(moreNumbers)
/**
* 这个cache()比较重要,他的意思是缓存中的内容可能会丢失,因此依赖关系树不会被丢弃。
* cache 和 checkpoint 之间有一个重大的区别,cache 将 RDD 以及 RDD 的血统(记录了这个RDD如何产生)缓存到内存中,
* 当缓存的 RDD 失效的时候(如内存损坏),它们可以通过血统重新计算来进行恢复。
* 但是 checkpoint 将 RDD 缓存到了 HDFS 中,同时忽略了它的血统(也就是RDD之前的那些依赖)。
* 为什么要丢掉依赖?因为可以利用 HDFS 多副本特性保证容错!
*/
moreNumbers.cache()
println("cached it: the dependencies don't change")
println(moreNumbers.toDebugString)
showDep(moreNumbers)
/**
* 下面这一部分演示了checkpoint的作用
* 有时候,Transformation 的 RDD 非常多或者具体 Transformation 产生的 RDD 本身计算特别复杂和耗时,此时我们必须考虑对计算结果数据进行持久化。与 persist 不同,persist 是优先将结果放入内存,内存不够的情况下,会放在磁盘。无论是放内存还是磁盘,都是不可靠的。Checkpoint 的产生就是为了相对而言更加可靠的持久化数据。
* 1. Checkpoint 可以指定把数据放在本地并且是多副本的方式,但是正常的生产环境下是放在 HDFS 上的,这就保证了中间计算结果持久化的高可靠性。
* 2. 在进行 RDD 的 Checkpoint 的时候其所依赖的所有的 RDD 都会从计算链条中清空掉
* 3. 作为最佳实践,一般在进行 checkpoint 方法调用前通常都要进行 persist 来把当前 RDD 的数据持久化到内存或者磁盘上,这是因为 checkpoint 是 Lazy 级别的,必须有 Job 的执行且在 Job执行后才会从后往前回溯哪个 RDD 进行了 checkpoint 标记,然后对标记了要进行 checkpoint 的 RDD 新启动一个 Job 执行具体的 Checkpoint 的过程。
* 4. Checkpoint 改变了 RDD 的 Lineage。
* 5. checkpoint 是另外启动一个 Job,并重新计算。而不是复用计算完的结果。因此建议在 checkpoint 之前进行 cache 操作。
*/
println("has RDD 'moreNumbers' been checkpointed? : " + moreNumbers.isCheckpointed)
// set moreNumbers up to be checkpointed
sc.setCheckpointDir("/tmp/sparkcps")
moreNumbers.checkpoint()
// it will only happen after we force the values to be computed
println("NOW has it been checkpointed? : " + moreNumbers.isCheckpointed)
moreNumbers.count()
println("NOW has it been checkpointed? : " + moreNumbers.isCheckpointed)
println(moreNumbers.toDebugString)
showDep(moreNumbers)
// again, calculations are not done until strictly necessary
println("this shouldn't throw an exception")
val thisWillBlowUp = numbers map {
case (7) => { throw new Exception }
case (n) => n
}
// notice it didn't blow up yet even though there's a 7
println("the exception should get thrown now")
try {
println(thisWillBlowUp.count())
} catch {
case (e: Exception) => println("Yep, it blew up now")
}
}
}
Githup项目LearningSpark代码讲解(二)
最新推荐文章于 2024-05-05 05:25:47 发布