Spark指南——第二章:SparkCore——RDD概述(1)


本节学习SparkCore,SparkCore主要学习RDD的相关知识。

一、RDD概述

RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,它是MapReduce模型的扩展和延伸,但它解决了MapReduce的缺陷:在并行计算阶段高效地进行数据共享。运用高效的数据共享概念和类似于MapReduce的操作方式,使得并行计算能够高效地进行,并可以在特定的系统中得到关键的优化。

RDD是Spark中最基本的数据抽象。代码中是一个抽象类,它代表一个不可变可分区里面的元素可并行计算的集合。

二、RDD的属性

每个RDD具有以下五个主要内部属性

* Internally, each RDD is characterized by five main properties:
*
*  - 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)
*
  1. 一组分区(Partition)列表,即数据集的基本组成单位;
  2. 作用在每个数据分区的计算函数
  3. 描述与其他RDD的依赖关系列表
  4. (可选)一个为key-value RDD配置的Partitioner,即RDD的分片函数(如Hash分区);
  5. (可选)优先位置列表,根据数据的本地特性,指定了每个Partition分片的处理位置偏好。

1.RDD分区(Partitions)

RDD划分成很多的分区(Partition) 分布到集群的节点中,分区的数量决定RDD进行并行计算的粒度(并行度),RDD的并行度默认从父 RDD 传给子RDD。分区是一个逻辑概念。在RDD操作中用户可以使用Partitions方法获取RDD划分的分区数,当然用户也可以设定分区数目。如果没有指定将使用默认值,而默认数值是该程序所分配到CPU核数,如果是从HDFS文件创建,默认为文件的数据块数。

  • 示例1:使用textFile方法获取spark根目录的文件,未设置分区数,默认是该程序所分配到CPU核数
    在这里插入图片描述
  • 示例2: 显示的设置RDD为4个分区
    在这里插入图片描述

2.RDD分区计算函数

每个分区都会有计算函数,Spark的RDD的计算函数是以分片为基本单位的,每个RDD都会实现compute函数,对具体的分片进行计算,RDD中的分片是并行的,所以是分布式并行计算。有一点非
常重要,就是由于RDD有前后依赖关系,遇到宽依赖关系,例如,遇到reduceBykey等宽依赖操作的算子,Spark 将根据宽依赖划分Stage, Stage 内部通过Pipeline 操作,通过BlockManager获取相关的数据,因为具体的split要从外界读数据,也要把具体的计算结果写入外界,所以用了一个管理器,具体的split 都会映射成BlockManager的Block,而具体split会被函数处理,函数处理的具体形式是以任务的形式进行的。

3.RDD依赖关系(Dependencise)

在RDD中将依赖划分成了两种类型:窄依赖( Narrow Dependencies) 和宽依赖( Wide Dependencies),窄依赖是指每个父RDD的分区都至多被一个子RDD的分区使用,而宽依赖是多个子RDD的分区依赖一个父RDD的分区。例如,map操作是一种窄依赖,而join操作是–种宽依赖(除非父RDD已经基于Hash策略被划分过了)。如图:
在这里插入图片描述

4.RDD 分区函数Partitioner

key-value 数据类型的 RDD 分区器控制分区策略和分区数。分区划分对于Shuffle 类操作很关键,它决定了该操作的父RDD和子RDD之间的依赖类型。例如Join操作,如果协同划分的话,两个父RDD之间、父RDD与子RDD之间能形成一致的分区安排,即同一个Key保证被映射到同一个分区,这样就能形成窄依赖。反之,如果没有协同划分,导致宽依赖。这里所说的协同划分是指定分区划分器以产生前后一致的分区安排。

Spark默认提供两种划分器哈希分区划分器(HashPartitioner)范围分区划分器(RangePartitioner),且Partitioner 只存在于(K,V)类型的RDD中,对于非(K, V)类型的Partitioner值为None。

5. RDD优先位置(Preferred Locations)

在Spark形成任务有向无环图(DAG)时,会尽可能地把计算分配到靠近数据的位置,减少数据网络传输。当RDD产生的时候存在首选位置,如HadoopRDD分区的首选位置就是HDFS块所在的节点;当RDD分区被缓存,则计算应该发送到缓存分区所在的节点进行,再不然回溯RDD的“血统”一直找到具有首选位置属性的父RDD,并据此决定子RDD的位置。

三、RDD特点

RDD表示只读的分区的数据集,对RDD进行改动,只能通过RDD的转换操作,由一个RDD得到一个新的RDD,新的RDD包含了从其他RDD衍生所必需的信息。RDDs之间存在依赖,RDD的执行是按照血缘关系延时计算的。如果血缘关系较长,可以通过持久化RDD来切断血缘关系

四、RDD的使用说明

1.优先使用结构化API

Spark提供了两种API:高级API(结构化API)低级API。结构化API指以下三种核心分布式集合类型的API:Dataset类型DataFrame类型Spark SQL结构化API是在编写大部分数据处理程序时会用到的基础抽象概念。

Spark提供的结构化API在几乎所有场景下用户都应该尽量使用这些结构化API。但当这类高级API无法解决遇到的业务或工程问题的时候,就需要使用Spark的低级API,特别是弹性分布式数据集(RDD)SparkContext分布式共享变量(例如累加器和广播变量)。

RDD是Spark 1.X系列中主要的API,在2.X系列中仍然可以使用它,但是已不常用。但是无论是DataFrame还是Dataset,运行的所有Spark代码都将编译成一个RDD。因此,至少需要对RDD是什么以及如何使用RDD有一个基本的了解

2.何时使用RDD

一般来说,除非有非常非常明确的理由,否则不要手动创建RDD。它们是很低级的API,虽然它提供了大量的功能,但同时缺少结构化API中可用的许多优化。在绝大多数情况下,DataFrame比RDD更高效、更稳定并且具有更强的表达能力。当你需要对数据的物理分布进行细粒度控制(自定义数据分区)时,可能才需要使用RDD。

五、RDD的弹性解读(扩展)

RDD 作为弹性分布式数据集,它的弹性具体体现在以下七个方面

1.自动进行内存和磁盘数据存储的切换

Spark 会优先把数据放到内存中,如果内存实在放不下,会放到磁盘里面,不但能计算内存放下的数据,
也能计算内存放不下的数据 如果实际数据大于内存, 则要考虑数据放置策略和优化算法。
当应用程序内存不足时, Spark 应用程序将数据自动从内存存储切换到磁盘存储,以保障其高效运行。

2. 基于Lineage 血统 的高效容错机制

Lineage 是基于 Spark RDD 的依赖关系来完成的,每个操作只关联其父操作,
各个分片的数据之间互不影响,出现错误时只要恢复单个 Split的特定部分即可。
常规容错有两种方式:一个是数据检查点:另一个是记录数据的更新。

3. Task如果失败,会自动进行特定次数的重试

默认重试次数为4次。

4. Stage如果失败,会自动进行特定次数的重试

默认重试次数为4次,且可以直接运行计算失败的阶段,只计算失败的数据分片。

5. checkpoint 和persist (检查点和持久化 ),可主动或被动触发

6. 数据调度弹性, DAGScheduler、 TASKScheduler和资源管理无关

Spark 将执行模型抽象为通用的有向无环图计划(DAG ),这可以将多 Stage 的任务串联或并行执行,
从而不需要将 Stage 中间结果输出到 HDFS 中,当发生节点运行故障时,可有其他可用节点代替该故障节点运行。

7. 数据分片的高度弹性( coalesce)

Spark 进行数据分片时,默认将数据放在内存中,如果内存放不下, 部分会放在磁盘上进行保存。

七、任务划分

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

  • Application:初始化一个SparkContext即生成一个Application
  • Job:一个Action算子就会生成一个Job
  • Stage:根据RDD之间的依赖关系的不同将Job划分成不同的Stage,遇到一个宽依赖则划分一个Stage。
  • Task:Stage是一个TaskSet,将Stage划分的结果发送到不同的Executor执行即为一个Task。
    注意:Application->Job->Stage-> Task每一层都是1对n的关系。

八、RDD缓存

  • RDD通过persist方法cache方法可以将前面的计算结果缓存,默认情况下 persist() 会把数据以序列化的形式缓存在 JVM 的堆空间中。 但是并不是这两个方法被调用时立即缓存,而是触发后面的action时,该RDD将会被缓存在计算节点的内存中,并供后面重用。persist方法cache方法源码如下:
    /**
     * Persist this Dataset with the default storage level (`MEMORY_AND_DISK`).
     *
     * @group basic
     * @since 1.6.0
     */
    def persist(): this.type = {
      sparkSession.sharedState.cacheManager.cacheQuery(this)
      this
    }
    
    /**
     * Persist this Dataset with the default storage level (`MEMORY_AND_DISK`).
     *
     * @group basic
     * @since 1.6.0
     */
    def cache(): this.type = persist()
    
  • 通过查看源码发现cache最终也是调用了persist方法,默认的存储级别都是仅在内存存储一份,Spark的存储级别还有好多种,存储级别在object StorageLevel中定义的。
    object StorageLevel {
     val NONE = new StorageLevel(false, false, false, false)
     val DISK_ONLY = new StorageLevel(true, false, false, false)
     val DISK_ONLY_2 = new StorageLevel(true, false, false, false, 2)
     val MEMORY_ONLY = new StorageLevel(false, true, false, true)
     val MEMORY_ONLY_2 = new StorageLevel(false, true, false, true, 2)
     val MEMORY_ONLY_SER = new StorageLevel(false, true, false, false)
     val MEMORY_ONLY_SER_2 = new StorageLevel(false, true, false, false, 2)
     val MEMORY_AND_DISK = new StorageLevel(true, true, false, true)
     val MEMORY_AND_DISK_2 = new StorageLevel(true, true, false, true, 2)
     val MEMORY_AND_DISK_SER = new StorageLevel(true, true, false, false)
     val MEMORY_AND_DISK_SER_2 = new StorageLevel(true, true, false, false, 2)
     val OFF_HEAP = new StorageLevel(true, true, true, false, 1)
    }
    

九、CheckPoint

  • Spark中对于数据的保存除了持久化操作之外,还提供了一种检查点的机制,检查点(本质是通过将RDD写入Disk做检查点)是为了通过lineage做容错的辅助,lineage过长会造成容错成本过高,这样就不如在中间阶段做检查点容错,如果之后有节点出现问题而丢失分区,从做检查点的RDD开始重做Lineage,就会减少开销。检查点通过将数据写入到HDFS文件系统实现了RDD的检查点功能。
  • 为当前RDD设置检查点。该函数将会创建一个二进制的文件,并存储到checkpoint目录中,该目录是用SparkContext.setCheckpointDir()设置的。在checkpoint的过程中,该RDD的所有依赖于父RDD中的信息将全部被移除。对RDD进行checkpoint操作并不会马上被执行,必须执行Action操作才能触发。

十、where to go

第二章:SparkCore——RDD编程(2)

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 1024 设计师:白松林 返回首页