[ Spark ] RDD序列化.依赖.持久化

1 RDD序列化

​ 从计算的角度, 算子以外的代码都是在Driver端执行, 算子里面的代码都是在Executor端执行。算子内用到算子外的数据,这样就形成了闭包的效果,算子外的数据需要序列化传值给Executor端执行,闭包检测

​ 这里的闭包指:就算Executor端的代码一行没走,只要使用了算子外的对象,就算闭包,即改变了变量的生命周期,就需要闭包检测

实例:

object Spark01_RDD_Serial {
  def main(args: Array[String]): Unit = {
    val sparkConf = new SparkConf().setMaster("local[4]").setAppName("Serial")
    val sc = new SparkContext(sparkConf)

    val rdd: RDD[String] = sc.makeRDD(Array("hello world", "hello spark", "hive", "atguigu"))

    //3.1创建一个Search对象
    val search = new Search("hello")

    //3.2 函数传递,打印:ERROR Task not serializable
//    search.getMatch1(rdd).collect().foreach(println)

    //3.3 属性传递,打印:ERROR Task not serializable
    search.getMatch2(rdd).collect().foreach(println)


    sc.stop()
  }
}
//构造参数作为类的私有属性,一旦使用就需要类进行闭包检测
class Search(query:String){

  def isMatch(s: String): Boolean = {
    s.contains(query)
  }

  // 函数序列化案例
  def getMatch1 (rdd: RDD[String]): RDD[String] = {
    //rdd.filter(this.isMatch)
    rdd.filter(isMatch)
  }

  // 属性序列化案例
  def getMatch2(rdd: RDD[String]): RDD[String] = {
    //rdd.filter(x => x.contains(this.query))
//    rdd.filter(x => x.contains(query))
    //1.混入序列化特质2.样例类3.将构造参数值传递给变量
    val q = query
    rdd.filter(x => x.contains(q))
  }
}

2 RDD依赖关系

​ 相邻的新的RDD依赖旧的RDD,多个连续的RDD的依赖关系,称之为血缘,同时RDD会保存血缘关系,但不会保存数据,

​ 容错:RDD部分分区数据丢失时,根据血缘关系将数据源重新读取进行计算

2.1 RDD窄依赖

​ 一个任务就能对该分区的所有数据执行完全部逻辑且得到正确结果,新的RDD一个分区的数据依赖于旧的RDD一个分区的数据

image-20220613205457835

2.2 RDD宽依赖

​ 宽依赖表示同一个父RDD的Partition被多个子RDD的Partition依赖,会引起Shuffle,新的RDD一个分区的数据依赖于旧的RDD多个分区的数据

​ 任务数相较窄依赖增加,且操作分为多个阶段,前一个阶段的task执行完才往下

image-20220613205651421

2.3 RDD阶段划分

​ DAG(Directed Acyclic Graph)有向无环图是由点和线组成的拓扑图形,该图形具有方向,不会闭环。例如,DAG记录了RDD的转换过程和任务的阶段。

​ 当RDD中存在shuffle依赖时,阶段自动加一,因此阶段数=shuffle依赖的数量+1(result依赖)

image-20220613205853708

2.4 RDD任务划分

RDD任务切分中间分为:Application、Job、Stage和Task
Application:初始化一个SparkContext(上下文环境对象)即生成一个Application;
Job:一个Action算子就会生成一个Job;
Stage:Stage等于宽依赖(ShuffleDependency)的个数加1;
Task:一个Stage阶段中,最后一个RDD的分区个数就是Task的个数。
注意:Application->Job->Stage->Task每一层都是1对n的关系。

image-20220613210035936

3 RDD持久化

3.1 RDD Cache缓存

​ RDD通过Cache或者Persist方法将前面的计算结果缓存,默认情况下会把数据以序列化的形式缓存在JVM的堆内存中。但是并不是这两个方法被调用时立即缓存,而是触发后面的action算子时,该RDD将会被缓存在计算节点的内存中,并供后面重用。

​ Spark会自动对一些Shuffle操作的中间数据做持久化操作(比如:reduceByKey)。这样做的目的是为了当一个节点Shuffle失败了避免重新计算整个输入。但是,在实际使用的时候,如果想重用数据,仍然建议调用persist或cache。

3.2 RDD CheckPoint

​ 将RDD中间结果写入磁盘,由于血缘依赖过长会造成容错成本过高,在中间阶段做检查点容错,如果检查点之后有节点出现问题,可以从检查点开始重做血缘,减少了开销。
​ 对RDD进行checkpoint操作并不会马上被执行,必须执行Action操作才能触发。

3.3 两者区别

​ cache:将数据临时存储在内存中进行数据重用,会在血缘关系中添加新的依赖.一旦出现问题,可以从头读取数据

​ persist:将数据临时存储在磁盘文件中进行数据重用,涉及到磁盘IO,性能较低,但是数据安全,如果作业执行完毕,临时保存的数据文件就会丢失

​ checkpoint:将数据长久地保存在磁盘文件中进行数据重用,涉及到磁盘IO,性能较低,但是数据安全,所以一般情况下,是需要和cache联合使用,执行过程中,会切断血缘关系,重新建立新的血缘关系,checkpoint等同于改变数据源

4 自定义分区器

object Spark01_RDD_Part {
  def main(args: Array[String]): Unit = {
    val sparkConf = new SparkConf().setMaster("local[4]").setAppName("part")
    val sc = new SparkContext(sparkConf)

    val rdd = sc.makeRDD(List(
      ("nba", "xxxxxxxxxx"),
      ("cba", "xxxxxxxxxx"),
      ("wnba", "xxxxxxxxxx"),
      ("nba", "xxxxxxxxxx")
    ), 3)

    val partRDD = rdd.partitionBy(new MyPartitioner)
    partRDD.saveAsTextFile("output")

    sc.stop()
  }
}
class MyPartitioner extends Partitioner{
  override def numPartitions: Int = 3

  override def getPartition(key: Any): Int = {
    key match {
      case "nba" =>0
      case "cba" =>1
      case _ =>2
    }
  }
}

5 RDD文件读取与保存

//文件保存
val sparkConf = new SparkConf().setMaster("local[4]").setAppName("part")
val sc = new SparkContext(sparkConf)

val rdd = sc.makeRDD(List(
  ("a", 1),
  ("b", 2),
  ("c", 3)
))

rdd.saveAsTextFile("output1")
rdd.saveAsObjectFile("output2")
rdd.saveAsSequenceFile("output3")
    
//文件读取
sc.textFile("output1").collect().foreach(println)
sc.objectFile("output2").collect().foreach(println)
sc.sequenceFile[String,Int]("output3").collect().foreach(println)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值