SparkCore 基础概念

RDD

RDD的概念

1.RDD是什么?

RDD是spark中的一个最基本的抽象,代表着一个不可变、可分区、可以并行计算的分布式数据集;

2.为什么不用MapReduce?

  1. MapReduce的缺陷:
    1.无法在并行计算的各个阶段进行有效的数据共享;
    2.启动时间较长,MapReduce过程几乎什么都不做,光启动就需要20-30s;
    3.MapReduce会频繁地对磁盘进行读写操作,然而这些磁盘I/O并不是必须的;
  2. 为什么使用RDD?
    RDD具有数据流模型的特点:自动容错、位置感知性调度和可伸缩性。RDD允许用户在执行多个查询时显式地将工作集缓存在内存中,后续的查询能够重用工作集,这极大地提升了查询速度。

图源:《Hadoop应用架构》

3.RDD源码中的描述

 *  - A list of partitions  
 *  - A function for computing each split
 *  - A list of dependencies on other RDDs  
 *  - Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned)     
 *  - Optionally, a list of preferred locations to compute each split on (e.g. block locations for*    an HDFS file)     

RDD的特点:

    1. 一系列分区
    1. 会有一个函数作用在每个切片上
    1. RDD和RDD之间存在依赖关系
    1. 如果RDD中装的是KV类型的,那么shuffle时会有一个分区器。默认是HashPartitioner
    1. 如果只从HDFS中读取数据,会感知数据则位置,将Executor启动在数据所在的机器上

4.理解RDD

  • RDD是一个基本的抽象,是对存储在分布式文件系统上的数据操作进行代理
    • 理解:RDD并不存储需要计算数据,而是一个代理,你对RDD进行的操作,他会在Driver端转换成Task,下发到Executor中,计算分散在集群中的数据。RDD是抽象的,并不存储数据,而是封装记录你对数据的操作。

在这里插入图片描述

  • 分片(Partition),数据集的基本组成单元。对于RDD来说,每个分片都会被一个计算任务处理,并决定并行计算的粒度。RDD默认分片数目就是CPU Core的数目。
  • 分区,Spark中RDD计算是以分区为单位的,每个RDD都会实现compute函数以达到这个目的。
  • RDD之间的依赖关系。RDD每次转换都会生成一个新的RDD,所以RDD之间会形成前后依赖关系。在部分分区丢失数据时,Spark可以通过这个依赖关系重新计算丢失的分区数据。RDD的依赖关系包括:宽依赖和窄依赖。
  • Partitioner,即RDD的分片函数。Spark中实现了两种类型的分片函数,一个是基于哈希的HashPartitioner,另外一个是基于范围的RangePartitioner。只有对于key-value的RDD,才会有Partitioner,非key-value的RDD的Parititioner的值是None。Partitioner函数不但决定了RDD本身的分片数量,也决定了parent RDD Shuffle输出时的分片数量。
  • 通过向NameNode请求元数据,获得每个Partition的优先位置(存储在列表中)。Spark在进行任务调度时,会尽可能地将计算任务分配到其所要处理的数据块位置。

5.RDD的基础操作

RDD算子的操作包括两种类型:Transformation和Action。

  1. 创建操作

RDD的初始创建都是由SparkContext来负责的

 1. 通过外部的存储系统创建RDD(如hdfs)  如:
val rdd1 = sc.textFile("hdfs://node1:8020/wordCount.txt")

 2. 将Driver的Scala集合通过并行化的方式编程RDD(一般用于试验、测验)如:
val rdd1 = sc.parallelize(Array(1,2,3,4,5,6,7,8)

 3. 调用一个已经存在了的RDD的Transformation,会生成一个新的RDD,如:
 val rdd2= rdd1.map(_*10)
  1. 转换操作:
    Transformation,将一个RDD通过一定的操作变换成另一个RDD

      1.RDD中的所有转换都是延迟加载的,也就是说,它们并不会直接计算结果。相反的,它们只是记住这些应用到基础数据集(例如一个文件)上的转换动作。只有当发生一个要求返回结果给Driver的动作时,这些转换才会真正运行。
      
      2. Transformation的特点:
     	 - lazy    
     	 - 生成新的RDD
      3. 常见的Transformation
      aggregateByKey,reduceByKey,filter,flatMap,map ,mapPartition,mapPartitionWithIndex 
    

**特别注意:**如果在Driver端通过Scala集合并行化创建RDD,并没有指定分区,则默认为CPU核数;如果从HDFS中读取数据创建RDD,那么每个分区对应一个Task

  1. 缓存操作:

对RDD进行持久化操作,可以让RDD保存在磁盘或者内存中,以便后续重复使用。

  • cache方法,没有生成新的RDD,也没有触发任务执行,只会标记该RDD分区对应的数据,在第一次触发Action时放入到内存
  • cache方法实质上是调用 RDD.persist() 让 Spark 把这个 RDD 缓存下来,persist方法的StorageLevel方法定义存储规则,存储级别包括仅内存( memory only)、仅磁盘( disk only)、堆外内存( off heap)
//四个参数分别:第一个参数,放到磁盘 第二个参数,放到内存  第三个参数,磁盘中的数据,不是以java对象的方式保存  第四个参数,内存中的数据,以java对象的方式保存
val MEMORY_AND_DISK = new StorageLevel(true, true, false, true)
...
...
  • 什么时候进行cache?
    1.要求的计算速度快
    2.集群的资源要足够大
    3.cache的数据会多次的触发Action
    4.先进行过滤,然后将缩小范围的数据在cache到内存

  • 检查点机制

  • 检查点是将 RDD 保存到磁盘上的操作,以便将来对此 RDD 的引用能直接访问磁盘上的那些中间结果,而不需要从其源头重新计算 RDD。 它与缓存类似,只是它不存储在内存中,只存在磁盘上

  • 注意的是,checkpoint方法并没有生成新的RDD,也没有触发Action,只会在触发Action时将数据保存到HDFS中

    • 什么时候使用checkpoint:
    • 迭代计算,要求保证数据安全(机器学习计算中间结果)
    • 对速度要求不高(跟cache到内存进行对比)
    • 将中间结果保存到hdfs

例:当引用此 RDD 时,它将从检查点直接获得而非从源数据重新计算而来

//1.设置checkpoint目录
spark.sparkContext.setCheckpointDir("/some/path/for/checkpointing")
//2.计算得到中间结果
//3.将中间结果checkpoint到指定的目录,rdd.checkpoint
words.checkpoint() 
  1. 行动操作(action)

Spark是惰性计算,所以所有对RDD 进行行动操作action都会触发Spark执行器的运行,从而产生最后的结果。行动操作会对 RDD 计算出一个结果,并把结果返回到驱动器程序中,或把结果存储到外部存储系统(如 HDFS)中。、
常见action操作:
collect,aggregate,saveAsTextFile, foreach ,foreachPartition

6.RDD执行流程

  1. Drvier端跟Master建立连接并申请资源,设置每个Executor需要的计算资源
  2. Master再进行资源调度后,跟worker进行RPC通信,让worker启动Executor,并与Driver进行通信
  3. 扫描初始化程序,RDD触发Action后,根据最后这个RDD从后往前推断依赖关系,遇到Shuffle就切分Stage,会递归切分,递归的出口是RDD没有存在父RDD了
  4. DAGScheduler切分完Stage后,按顺序提交Stage,Stage会产生Task,一个Stage会生产很多业务逻辑相同的Task,然后以TaskSet形式传送给TaskScheduler,然后TaskScheduler将Task序列化,根据资源情况,发送给Executor
  5. Executor接收到Task后,将Task反序列化,然后将Task用一个实现了Runnable接口的实现类包装起来,然后将该包装类丢到线程池中,然后包装类的run方法就会被执行,进而调用Task的计算逻辑
    在这里插入图片描述
    注意点:
  • 切分Stage:AGScheduler:将一个DAG切分成一到多个Stage,DAGScheduler切分的依据是Shuffle(宽依赖)
    • 为什么要切分Stage?
    • 一个复杂的业务逻辑(将多台机器上具有相同属性的数据聚合到一台机器上:shuffle),如果有shuffle,那么就意味着前面阶段产生的结果后,才能执行下一个阶段,下一个阶段的计算要依赖上一个阶段的数据。
    • 在同一个Stage中,会有多个算子,可以合并在一起,我们称其为pipeline(流水线:严格按照流程、顺序执行)
    • 宽依赖和窄依赖::一个RDD的一个分区的数据分发给不同的RDD,则为宽依赖,要进行Shuffle,反之为窄依赖(即使存在分发给子RDD的可能就是宽依赖)

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值