4.大数据技术之SparkCore_第二章:Action算子操作

2.4 Action

2.4.1 reduce(func)案例

1. 作用:通过func函数聚集RDD中的所有元素,先聚合分区内数据,再聚合分区间数据。

2. 需求:创建一个RDD,将所有元素聚合得到结果

(1)创建一个RDD[Int]

scala> val rdd1 = sc.makeRDD(1 to 10,2)

rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[85] at makeRDD at <console>:24

(2)聚合RDD[Int]所有元素

scala> rdd1.reduce(_+_)

res50: Int = 55

(3)创建一个RDD[String]

scala> val rdd2 = sc.makeRDD(Array(("a",1),("a",3),("c",3),("d",5)))

rdd2: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[86] at makeRDD at <console>:24

(4)聚合RDD[String]所有数据

scala> rdd2.reduce((x,y)=>(x._1 + y._1,x._2 + y._2))

res51: (String, Int) = (adca,12)

2.4.2 collect()案例

1. 作用:在驱动程序中,以数组的形式返回数据集的所有元素。

2. 需求:创建一个RDD,并将RDD内容收集到Driver端打印

(1)创建一个RDD

scala> val rdd = sc.parallelize(1 to 10)

rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at <console>:24

(2)将结果收集到Driver端

scala> rdd.collect

res0: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)   

2.4.3 count()案例

1. 作用:返回RDD中元素的个数

2. 需求:创建一个RDD,统计该RDD的条数

(1)创建一个RDD

scala> val rdd = sc.parallelize(1 to 10)

rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at <console>:24

(2)统计该RDD的条数

scala> rdd.count

res1: Long = 10

2.4.4 first()案例

1. 作用:返回RDD中的第一个元素

2. 需求:创建一个RDD,返回该RDD中的第一个元素

(1)创建一个RDD

scala> val rdd = sc.parallelize(1 to 10)

rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at <console>:24

(2)统计该RDD的条数

scala> rdd.first

res2: Int = 1

2.4.5 take(n)案例

1. 作用:返回一个由RDD的前n个元素组成的数组

2. 需求:创建一个RDD,统计该RDD的条数

(1)创建一个RDD

scala> val rdd = sc.parallelize(Array(2,5,4,6,8,3))

rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[2] at parallelize at <console>:24

(2)统计该RDD的条数

scala> rdd.take(3)

res10: Array[Int] = Array(2, 5, 4)

2.4.6 takeOrdered(n)案例

1. 作用:返回该RDD排序后的前n个元素组成的数组

2. 需求:创建一个RDD,统计该RDD的条数

(1)创建一个RDD

scala> val rdd = sc.parallelize(Array(2,5,4,6,8,3))

rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[2] at parallelize at <console>:24

(2)统计该RDD的条数

scala> rdd.takeOrdered(3)

res18: Array[Int] = Array(2, 3, 4)

2.4.7 aggregate案例

1. 参数:(zeroValue: U)(seqOp: (U, T) ⇒ U, combOp: (U, U) ⇒ U)

2. 作用:aggregate函数将每个分区里面的元素通过seqOp和初始值进行聚合,然后用combine函数将每个分区的结果和初始值(zeroValue)进行combine操作。这个函数最终返回的类型不需要和RDD中元素类型一致。

3. 需求:创建一个RDD,将所有元素相加得到结果

(1)创建一个RDD

scala> var rdd1 = sc.makeRDD(1 to 10,2)

rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[88] at makeRDD at <console>:24

(2)将该RDD所有元素相加得到结果

scala> rdd.aggregate(0)(_+_,_+_)

res22: Int = 55

2.4.8 fold(num)(func)案例

1. 作用:折叠操作,aggregate的简化操作,seqop和combop一样。

2. 需求:创建一个RDD,将所有元素相加得到结果

(1)创建一个RDD

scala> var rdd1 = sc.makeRDD(1 to 10,2)

rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[88] at makeRDD at <console>:24

(2)将该RDD所有元素相加得到结果

scala> rdd.fold(0)(_+_)

res24: Int = 55

2.4.9 saveAsTextFile(path)

作用:将数据集的元素以textfile的形式保存到HDFS文件系统或者其他支持的文件系统,对于每个元素,Spark将会调用toString方法,将它装换为文件中的文本

2.4.10 saveAsSequenceFile(path) 

作用:将数据集中的元素以Hadoop sequencefile的格式保存到指定的目录下,可以使HDFS或者其他Hadoop支持的文件系统。

2.4.11 saveAsObjectFile(path) 

作用:用于将RDD中的元素序列化成对象,存储到文件中。

2.4.12 countByKey()案例

1. 作用:针对(K,V)类型的RDD,返回一个(K,Int)的map,表示每一个key对应的元素个数。

2. 需求:创建一个PairRDD,统计每种key的个数

(1)创建一个PairRDD

scala> val rdd = sc.parallelize(List((1,3),(1,2),(1,4),(2,3),(3,6),(3,8)),3)

rdd: org.apache.spark.rdd.RDD[(Int, Int)] = ParallelCollectionRDD[95] at parallelize at <console>:24

(2)统计每种key的个数

scala> rdd.countByKey

res63: scala.collection.Map[Int,Long] = Map(3 -> 2, 1 -> 3, 2 -> 1)

2.4.13 foreach(func)案例

1. 作用:在数据集的每一个元素上,运行函数func进行更新。

2. 需求:创建一个RDD,对每个元素进行打印

(1)创建一个RDD

scala> var rdd = sc.makeRDD(1 to 5,2)

rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[107] at makeRDD at <console>:24

(2)对该RDD每个元素进行打印

scala> rdd.foreach(println(_))

3

4

5

1

2

2.4.14 aggregate算子和aggregateByKey算子

Aggregate算子

 

scala> import scala.math._

import scala.math._

 

scala> var rdd1 = sc.parallelize(List("12","34","567","8901"),2)

rdd1: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[0] at parallelize at <console>:24

 

scala> def fun1(index:Int,iter:Iterator[String]):Iterator[String]={

     | iter.toList.map(x => "[partID: "+index+",value:"+x+"]").iterator}

fun1: (index: Int, iter: Iterator[String])Iterator[String]

 

scala> rdd1.mapPartitionsWithIndex(fun1).collect

res1: Array[String] = Array(

[partID: 0,value:12], [partID: 0,value:34],

[partID: 1,value:567], [partID: 1,value:8901])

 

scala> rdd1.aggregate("")((x,y)=>math.max(x.length,y.length).toString,(x,y)=>x+y)

 

scala> rdd1.aggregate("")((x,y)=>math.max(x.length,y.length).toString,(x,y)=>x+y)

res3: String = 42

 

scala> rdd1.aggregate("")((x,y)=>math.max(x.length,y.length).toString,(x,y)=>x+y)

res4: String = 24

 

分析:

第一个分区:“12”,“34”

    第一次比较:“”,“12”=2.toString  ==》 “2”

    第二次比较:“2”,“34”=2.toString  ==》 “2”

第二个分区:“567”,“8901”

    第一次比较:“”,“567”=3.toString  ==》“3”

    第二次比较:“3”,“8901”=4.toString  ==》 “4”

“24”或者“42”

 

scala> var rdd1 = sc.parallelize(List("12","23","345",""),2)

rdd1: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[2] at parallelize at <console>:24

 

scala> rdd1.mapPartitionsWithIndex(fun1).collect

res6: Array[String] = Array([partID: 0,value:12], [partID: 0,value:23], [partID: 1,value:345], [partID: 1,value:])

 

scala> rdd1.aggregate("")((x,y)=>math.min(x.length,y.length).toString,(x,y)=>x+y)

res7: String = 10

 

scala> rdd1.aggregate("")((x,y)=>math.min(x.length,y.length).toString,(x,y)=>x+y)

res9: String = 01

 

 

分析:

第一个分区:“12”,“23”

    第一次比较:“”,“12”=0.toString  ==》 “0”

    第二次比较:“0”,“23”=1.toString  ==》 “1”

第二个分区:“345”,“”

    第一次比较:“”,“345”=0.toString  ==》“0”

    第二次比较:“0”,“”=0.toString  ==》 “0”

“10”或者“01”

 

AggregateByKey算子

 

scala> import scala.math._

import scala.math._

 

scala> var rdd1 = sc.parallelize(List(("Tom",20),("Tom",25),("Plus",2),("Plus",18),("Make",30),("Make",20),("Tom",10)),2)

rdd1: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[10] at parallelize at <console>:24

 

 

 

scala> def fun2(index:Int,iter:Iterator[(String,Int)]):Iterator[String]={

     | iter.toList.map(x => "[partId: "+index+",value: "+x+"]").iterator}

fun2: (index: Int, iter: Iterator[(String, Int)])Iterator[String]

 

 

scala> rdd1.mapPartitionsWithIndex(fun2)

res18: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[8] at mapPartitionsWithIndex at <console>:29

 

scala> rdd1.mapPartitionsWithIndex(fun2).collect

res20: Array[String] = Array(

[partId: 0,value: (Tom,20)], [partId: 0,value: (Tom,25)], [partId: 0,value: (Plus,2)], [partId: 1,value: (Plus,18)], [partId: 1,value: (Make,30)], [partId: 1,value: (Make,20)], [partId: 1,value: (Tom,10)])

 

scala> rdd1.aggregateByKey(0)(math.max(_,_),_+_)

res21: org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[12] at aggregateByKey at <console>:27

 

scala> var rdd2 = rdd1.aggregateByKey(0)(math.max(_,_),_+_)

rdd2: org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[13] at aggregateByKey at <console>:26

 

scala> rdd2.collect

res22: Array[(String, Int)] = Array((Tom,35), (Plus,20), (Make,30))

 

第一个分区:(Tom,20),(Tom,25),(Plus,2)

max:Tom:25,Plus:2

第二个分区:(Plus,18), (Make,30),(Make,20),(Tom,10)

max:plus:18,Make:30,Tom:10

 

相加:Tom:35,Plus:20,Make:30

 

scala> var rdd2 = rdd1.aggregateByKey(0)(_+_,_+_)

rdd2: org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[14] at aggregateByKey at <console>:26

 

scala> rdd2.collect

res23: Array[(String, Int)] = Array((Tom,55), (Plus,20), (Make,50))

 

2.5 RDD中的函数传递(自定义算子,注意要序列化)

在实际开发中我们往往需要自己定义一些对于RDD的操作,那么此时需要主要的是,初始化工作是在Driver端进行的,而实际运行程序是在Executor端进行的,这就涉及到了跨进程通信,是需要序列化的。下面我们看几个例子:

2.5.1 传递一个方法

1.创建一个类

class Search(query:String){

 

//过滤出包含字符串的数据

  def isMatch(s: String): Boolean = {

    s.contains(query)

  }

 

//过滤出包含字符串的RDD

  def getMatch1 (rdd: RDD[String]): RDD[String] = {

    rdd.filter(isMatch)

  }

 

  //过滤出包含字符串的RDD

  def getMatche2(rdd: RDD[String]): RDD[String] = {

    rdd.filter(x => x.contains(query))

  }

 

}

2.创建Spark主程序

object SeriTest {

 

  def main(args: Array[String]): Unit = {

 

    //1.初始化配置信息及SparkContext

    val sparkConf: SparkConf = new SparkConf().setAppName("WordCount").setMaster("local[*]")

    val sc = new SparkContext(sparkConf)

 

//2.创建一个RDD

    val rdd: RDD[String] = sc.parallelize(Array("hadoop", "spark", "hive", "itstar"))

 

//3.创建一个Search对象

    val search = new Search("spark")

 

//4.运用第一个过滤函数并打印结果

    val match1: RDD[String] = search.getMatche1(rdd)

    match1.collect().foreach(println)

    }

}

3.运行程序

Exception in thread "main" org.apache.spark.SparkException: Task not serializable

    at org.apache.spark.util.ClosureCleaner$.ensureSerializable(ClosureCleaner.scala:298)

    at org.apache.spark.util.ClosureCleaner$.org$apache$spark$util$ClosureCleaner$$clean(ClosureCleaner.scala:288)

    at org.apache.spark.util.ClosureCleaner$.clean(ClosureCleaner.scala:108)

    at org.apache.spark.SparkContext.clean(SparkContext.scala:2101)

    at org.apache.spark.rdd.RDD$$anonfun$filter$1.apply(RDD.scala:387)

    at org.apache.spark.rdd.RDD$$anonfun$filter$1.apply(RDD.scala:386)

    at org.apache.spark.rdd.RDDOperationScope$.withScope(RDDOperationScope.scala:151)

    at org.apache.spark.rdd.RDDOperationScope$.withScope(RDDOperationScope.scala:112)

    at org.apache.spark.rdd.RDD.withScope(RDD.scala:362)

    at org.apache.spark.rdd.RDD.filter(RDD.scala:386)

    at com.itstar.Search.getMatche1(SeriTest.scala:39)

    at com.itstar.SeriTest$.main(SeriTest.scala:18)

    at com.itstar.SeriTest.main(SeriTest.scala)

Caused by: java.io.NotSerializableException: com.itstar.Search

4.问题说明

//过滤出包含字符串的RDD

  def getMatch1 (rdd: RDD[String]): RDD[String] = {

    rdd.filter(isMatch)

  }

在这个方法中所调用的方法isMatch()是定义在Search这个类中的,实际上调用的是this. isMatch(),this表示Search这个类的对象,程序在运行过程中需要将Search对象序列化以后传递到Executor端。

5.解决方案

使类继承scala.Serializable即可。

class Search() extends Serializable{...}

2.5.2 传递一个属性

1.创建Spark主程序

object TransmitTest {

 

  def main(args: Array[String]): Unit = {

 

    //1.初始化配置信息及SparkContext

    val sparkConf: SparkConf = new SparkConf().setAppName("WordCount").setMaster("local[*]")

    val sc = new SparkContext(sparkConf)

 

//2.创建一个RDD

    val rdd: RDD[String] = sc.parallelize(Array("hadoop", "spark", "hive", "itstar"))

 

//3.创建一个Search对象

    val search = new Search(“spark”)

 

//4.运用第一个过滤函数并打印结果

    val match1: RDD[String] = search.getMatche2(rdd)

    match1.collect().foreach(println)

    }

}

2.运行程序

Exception in thread "main" org.apache.spark.SparkException: Task not serializable

    at org.apache.spark.util.ClosureCleaner$.ensureSerializable(ClosureCleaner.scala:298)

    at org.apache.spark.util.ClosureCleaner$.org$apache$spark$util$ClosureCleaner$$clean(ClosureCleaner.scala:288)

    at org.apache.spark.util.ClosureCleaner$.clean(ClosureCleaner.scala:108)

    at org.apache.spark.SparkContext.clean(SparkContext.scala:2101)

    at org.apache.spark.rdd.RDD$$anonfun$filter$1.apply(RDD.scala:387)

    at org.apache.spark.rdd.RDD$$anonfun$filter$1.apply(RDD.scala:386)

    at org.apache.spark.rdd.RDDOperationScope$.withScope(RDDOperationScope.scala:151)

    at org.apache.spark.rdd.RDDOperationScope$.withScope(RDDOperationScope.scala:112)

    at org.apache.spark.rdd.RDD.withScope(RDD.scala:362)

    at org.apache.spark.rdd.RDD.filter(RDD.scala:386)

    at com.itstar.Search.getMatche1(SeriTest.scala:39)

    at com.itstar.SeriTest$.main(SeriTest.scala:18)

    at com.itstar.SeriTest.main(SeriTest.scala)

Caused by: java.io.NotSerializableException: com.itstar.Search

3.问题说明

  //过滤出包含字符串的RDD

  def getMatche2(rdd: RDD[String]): RDD[String] = {

    rdd.filter(x => x.contains(query))

  }

在这个方法中所调用的方法query是定义在Search这个类中的字段,实际上调用的是this. query,this表示Search这个类的对象,程序在运行过程中需要将Search对象序列化以后传递到Executor端。

4.解决方案

1)使类继承scala.Serializable即可。

class Search() extends Serializable{...}

2)将类变量query赋值给局部变量

修改getMatche2为

  //过滤出包含字符串的RDD

  def getMatche2(rdd: RDD[String]): RDD[String] = {

    val query_ : String = this.query//将类变量赋值给局部变量

    rdd.filter(x => x.contains(query_))

  }

2.6 RDD依赖关系

2.6.1 Lineage

RDD只支持粗粒度转换,即在大量记录上执行的单个操作。将创建RDD的一系列Lineage(血统)记录下来,以便恢复丢失的分区。RDD的Lineage会记录RDD的元数据信息和转换行为,当该RDD的部分分区数据丢失时,它可以根据这些信息来重新运算和恢复丢失的数据分区。

 

(1)读取一个HDFS文件并将其中内容映射成一个个元组

scala> val wordAndOne = sc.textFile("/fruit.tsv").flatMap(_.split("\t")).map((_,1))

wordAndOne: org.apache.spark.rdd.RDD[(String, Int)] = MapPartitionsRDD[22] at map at <console>:24

(2)统计每一种key对应的个数

scala> val wordAndCount = wordAndOne.reduceByKey(_+_)

wordAndCount: org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[23] at reduceByKey at <console>:26

(3)查看“wordAndOne”的Lineage

scala> wordAndOne.toDebugString

res5: String =

(2) MapPartitionsRDD[22] at map at <console>:24 []

 |  MapPartitionsRDD[21] at flatMap at <console>:24 []

 |  /fruit.tsv MapPartitionsRDD[20] at textFile at <console>:24 []

 |  /fruit.tsv HadoopRDD[19] at textFile at <console>:24 []

(4)查看“wordAndCount”的Lineage

scala> wordAndCount.toDebugString

res6: String =

(2) ShuffledRDD[23] at reduceByKey at <console>:26 []

 +-(2) MapPartitionsRDD[22] at map at <console>:24 []

    |  MapPartitionsRDD[21] at flatMap at <console>:24 []

    |  /fruit.tsv MapPartitionsRDD[20] at textFile at <console>:24 []

    |  /fruit.tsv HadoopRDD[19] at textFile at <console>:24 []

(5)查看“wordAndOne”的依赖类型

scala> wordAndOne.dependencies

res7: Seq[org.apache.spark.Dependency[_]] = List(org.apache.spark.OneToOneDependency@5d5db92b)

(6)查看“wordAndCount”的依赖类型

scala> wordAndCount.dependencies

res8: Seq[org.apache.spark.Dependency[_]] = List(org.apache.spark.ShuffleDependency@63f3e6a8)

注意:RDD和它依赖的父RDD(s)的关系有两种不同的类型,即窄依赖(narrow dependency)和宽依赖(wide dependency)。

2.6.2 窄依赖

窄依赖指的是每一个父RDD的Partition最多被子RDD的一个Partition使用,窄依赖我们形象的比喻为独生子女

2.6.3 宽依赖

宽依赖指的是多个子RDD的Partition会依赖同一个父RDD的Partition,会引起shuffle,总结:宽依赖我们形象的比喻为超生

 

2.6.4 DAG

DAG(Directed Acyclic Graph)叫做有向无环图,原始的RDD通过一系列的转换就就形成了DAG,根据RDD之间的依赖关系的不同将DAG划分成不同的Stage,对于窄依赖,partition的转换处理在Stage中完成计算。对于宽依赖,由于有Shuffle的存在,只能在parent RDD处理完成后,才能开始接下来的计算,因此宽依赖是划分Stage的依据。

2.6.5 任务划分(面试重点)

RDD任务切分中间分为:Application、Job、Stage和Task

1)Application:初始化一个SparkContext即生成一个Application

2)Job:一个Action算子就会生成一个Job

3)Stage:根据RDD之间的依赖关系的不同将Job划分成不同的Stage,遇到一个宽依赖则划分一个Stage。

4)Task:Stage是一个TaskSet,将Stage划分的结果发送到不同的Executor执行即为一个Task。

注意:Application->Job->Stage->Task每一层都是1对n的关系。

2.7 RDD缓存

RDD通过persist方法或cache方法可以将前面的计算结果缓存,默认情况下 persist() 会把数据以序列化的形式缓存在 JVM 的堆空间中。

但是并不是这两个方法被调用时立即缓存,而是触发后面的action时,该RDD将会被缓存在计算节点的内存中,并供后面重用。

通过查看源码发现cache最终也是调用了persist方法,默认的存储级别都是仅在内存存储一份,Spark的存储级别还有好多种,存储级别在object StorageLevel中定义的。

在存储级别的末尾加上“_2”来把持久化数据存为两份

缓存有可能丢失,或者存储存储于内存的数据由于内存不足而被删除,RDD的缓存容错机制保证了即使缓存丢失也能保证计算的正确执行。通过基于RDD的一系列转换,丢失的数据会被重算,由于RDD的各个Partition是相对独立的,因此只需要计算丢失的部分即可,并不需要重算全部Partition。

(1)创建一个RDD

scala> val rdd = sc.makeRDD(Array("itstar"))

rdd: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[19] at makeRDD at <console>:25

(2)将RDD转换为携带当前时间戳做缓存

scala> val nocache = rdd.map(_.toString+System.currentTimeMillis)

nocache: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[20] at map at <console>:27

(3)多次打印结果

scala> nocache.collect

res0: Array[String] = Array(itstar1538978275359)

 

scala> nocache.collect

res1: Array[String] = Array(itstar1538978282416)

 

scala> nocache.collect

res2: Array[String] = Array(itstar1538978283199)

(4)将RDD转换为携带当前时间戳做缓存

scala> val cache =  rdd.map(_.toString+System.currentTimeMillis).cache

cache: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[21] at map at <console>:27

(5)多次打印做了缓存的结果

scala> cache.collect

res3: Array[String] = Array(itstar1538978435705)                                   

 

scala> cache.collect

res4: Array[String] = Array(itstar1538978435705)

 

scala> cache.collect

res5: Array[String] = Array(itstar1538978435705)

2.8 RDD CheckPoint

Spark中对于数据的保存除了持久化操作之外,还提供了一种检查点的机制,检查点(本质是通过将RDD写入Disk做检查点)是为了通过lineage做容错的辅助,lineage过长会造成容错成本过高,这样就不如在中间阶段做检查点容错,如果之后有节点出现问题而丢失分区,从做检查点的RDD开始重做Lineage,就会减少开销。检查点通过将数据写入到HDFS文件系统实现了RDD的检查点功能。

为当前RDD设置检查点。该函数将会创建一个二进制的文件,并存储到checkpoint目录中,该目录是用SparkContext.setCheckpointDir()设置的。在checkpoint的过程中,该RDD的所有依赖于父RDD中的信息将全部被移除。对RDD进行checkpoint操作并不会马上被执行,必须执行Action操作才能触发。

案例实操:

(1)设置检查点

scala> sc.setCheckpointDir("hdfs://bigdata111:9000/checkpoint")

(2)创建一个RDD

scala> val rdd = sc.parallelize(Array("itstar"))

rdd: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[14] at parallelize at <console>:24

(3)将RDD转换为携带当前时间戳并做checkpoint

scala> val ch = rdd.map(_+System.currentTimeMillis)

ch: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[16] at map at <console>:26

 

scala> ch.checkpoint

(4)多次打印结果

scala> ch.collect

res55: Array[String] = Array(itstar1538981860336)

 

scala> ch.collect

res56: Array[String] = Array(itstar1538981860504)

 

scala> ch.collect

res57: Array[String] = Array(itstar1538981860504)

 

scala> ch.collect

res58: Array[String] = Array(itstar1538981860504)

cache和checkPoint的区别

1 cache是保存在内存,  ck是保存在磁盘

2 cache不会新创建job, ck会新创建job,ck在运行的第二次之后才开始缓存.    ---job提交次数, ck提交两次

3 源码中建议ck必须在调用任何行动算子之前运行ck,否则调用过行动算子ck失效 . cache就算调用了行动算子, 照样生效.   ---生效时机

4 通过rdd.toDebugString可以查看: cache不会切断血缘关系, ck会切断血缘关系

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值