大数据之SparkCore

本文介绍了Spark的基础知识,包括Spark的应用场景、Standalone集群架构,以及Spark程序的组成。重点讲解了RDD的核心概念,如其五大特性、创建方式和分区规则,详细阐述了RDD的各种算子,如转换算子、触发算子,并讨论了Spark的容错机制,包括持久化和检查点机制。此外,还提及了广播变量和Spark程序的运行流程。
摘要由CSDN通过智能技术生成

目录

1. Spark基础认识

1.1 Spark的应用及使用

1.2 Spark的Standalone集群架构

2. Sparkcore核心设计: RDD

2.1 Spark程序核心概念

架构组成 [以Standalone集群为例]

程序组成: 分布式集群中,任何一个Spark程序都包含两种进程Driver和Executor

Executor进程: 计算进程,每一个Spark程序都至少有一个

架构与程序关系

2.2 RDD的设计及定义

2.3 RDD的五大特性

2.4 RDD的两种创建方式

2.5 RDD的分区规则

3. RDD常用算子

3.1 RDD算子分类

3.2 常用转换算子

3.3 常用触发算子

3.4 其他触发算子

3.5 其他转换算子

3.6 分组聚合算子

3.7 排序算子

3.8 TopN算子

3.9 重分区算子

4. Spark容错机制

4.1 Spark中RDD的数据如何保证数据的安全

4.2 persist缓存机制

4.3 checkpoint检查点机制

4.4 RDD的cache,persist和checkpoint有什么区别?

5. Spark高级特性

5.1 Broadcast Variables广播变量

5.2 Spark应用中的概念

5.3 一个Spark程序是如何运行的

5.4 Spark的宽窄依赖


1. Spark基础认识

1.1 Spark的应用及使用

  • 应用场景

    • 离线场景:实现离线数据仓库中的数据清洗、数据分析、即席查询等应用

    • 实时场景:实现实时数据流数据处理,相对而言功能和性能不是特别的完善,工作中建议使用Flink替代

  • 开发语言:Python、Scala、SQL、Java、R

    • Python

    • SQL

  • 运行模式:5种模式

    • 本地模式Local:一般用于做测试,不是分布式程序运行,只是启动1个进程运行所有Task运行

    • 集群模式Cluster:一般用于测试集群或者生产集群

      • Standalone:Spark自带的集群资源管理平台

      • YARN:可以将Spark程序提交到YARN上运行

      • Mesos:类似于YARN,国外用的多

      • K8s:基于分布式容器的资源管理平台

1.2 Spark的Standalone集群架构

  • 类比
概念MR+YARNSpark Standalone
主节点ResourceManagerMaster
从节点NodeManagerWorker
计算进程MapTask、ReduceTaskExecutor
  • 架构: 分布式主从架构

  • 功能: 提供分布式资源管理和任务调度

    • 主:Master:管理节点,类比于YARN中的RM:管理从节点,接客,资源管理和任务调度

    • 从:Worker:计算节点,类比于YARN中NM:利用自己节点的资源运行计算进程

2. Sparkcore核心设计: RDD

2.1 Spark程序核心概念

  • 架构组成 [以Standalone集群为例]

    • 主: Master: 管理节点: 管理从节点,资源管理和任务调度,接受客户端消息

    • 从: Worker: 计算节点: 利用自己节点资源运行计算任务

  • 程序组成: 分布式集群中,任何一个Spark程序都包含两种进程Driver和Executor

    • 程序提交以后,会先启动Driver进程

    • Driver进程: 驱动进程,每一个Spark程序都有一个

      • 向主节点申请资源去启动Executor进程

    • Driver会解析代码变成Task任务

      • Driver会将Task任务调度分配给Executor去运行,并且监控所有Task运行

  • Executor进程: 计算进程,每一个Spark程序都至少有一个

    • Executor进程会利用Worker节点的资源运行[1个RDD有100个分区 = 100个Task线程处理]

    • 所有Executor一旦启动成功,向Driver反向注册

  • 架构与程序关系

  • step1: 客户端提交程序给主节点

  • step2: 主节点会根据提交的参数在对应的位置启动Driver进程

  • step3: Driver向主节点申请启动Executor计算进程

  • step4: Master根据配置在Worker节点上启动对应的Executor

  • step5: 所有Executor启动成功以后会向Driver反向注册

  • step6: Driver解析代码,根据代码构建Task,分配给Executor运行,并监控所有Task

2.2 RDD的设计及定义

  • 定义: 弹性 分布式 数据集

  • 理解: 分布式的列表

    • 本质: 一个分布式的逻辑的概念,物理上代表多台节点上的多个分区的数据

      • 一个抽象的逻辑上的数据集合的概念,类似于Python中的list, 但RDD是分布式的

      • Python中的list:数据只存在于list构建的节点

      • Spark中的RDD:数据是分布式存储在多台节点上的

    • 功能: 用于SparkCore中构建分布式数据对象,实现分布式数据的存储,实现分布式的数据存储,是一个对应多个物理分区的数据集合,每个分区的数据可以存储在不同的节点上

      • RDD本质上是一个逻辑的概念,代表多台机器上的多个分区的数据

      • RDD就类似于HDFS中的文件,RDD的分区就类似于HDFS中的Block块

  • 实现

    • step1: SparkCore中所有的数据读取到程序中以后会存储在一个RDD中,将数据划分到多个分区中

    • step2: 所有的数据转换处理,都直接在代码中对RDD进行处理,底层会对RDD的每个分区进行并行处理

2.3 RDD的五大特性

特性一:每一个RDD都由一系列的分区构成
特性二:RDD的计算操作本质上是对RDD每个分区的计算
特性三:每个RDD都会保存与其他RDD之间的依赖关系:  血链机制或者血脉机制
特性四:可选的, 如果是二元组[KV]类型的RDD, 在Shuffle过程中可以自定义分区器
特性五:可选的, Spark程序运行时, Task的分配可以指定实现最优路径解:最优计算位置

2.4 RDD的两种创建方式

  • 读取数据: 将所有数据源的数据放入RDD中 -- SparkContext

  • 方式1: 并行化已存在的集合(列表,字典,元组...): sc.parallize(集合, numSlice=分区个数)

    • 功能:将一个集合转换为RDD

  • 方式2: 读取外部存储系统: sc.textFile, sc.wholeTextFile

    • 功能:读取外部存储系统的数据转换为RDD

2.5 RDD的分区规则

  • RDD 的分区数由什么决定
  • 读取数据:

    • 方式1: 并行化集合: parallelize

      • 没有指定:spark.default.parallelism参数值决定

      • 指定分区:指定几个,就是几个分区

    • 方式2: 读取外部系统: textFile

      • 没有指定:spark.default.parallelism和2取最小值得到最小分区数,最终也是根据文件大小来

        • 参数:spark.default.parallelism:用于指定没有父RDD的RDD的分区数

      • 指定分区:最小分区数,最少有这么多分区,具体的分区数可以根据HDFS分片规则来

  • 处理数据

    • 默认:子RDD的分区数 = 父RDD的分区数

    • 特殊:允许通过调用算子进行修改:repartition、coalesce、reduceByKey等

3. RDD常用算子

3.1 RDD算子分类

  • Tranformation算子: 转换算子

    • 功能: 用于实现对RDD的数据进行转换

    • 特点: 都是lazy模式的,一般不会触发job运行,算子返回值一定是RDD

    • 常见: map/filter/flatMap/reduceByKey/groupByKey/sortByKey

  • Action算子:触发算子

    • 功能:触发job的运行,用户对RDD的数据进行输出或者保存

    • 特点:一定会触发job的运行,返回值一定不是RDD

    • 常见:foreach、first、count、reduce、saveAsTextFile、collect、take

3.2 常用转换算子

  • map算子

    • 功能: 对RDD中每个元素调用一次参数中的函数,并将每次调入的返回值直接放入一个新的RDD中

    • 分类: 转换算子

    • 场景: 一对一的转换,需要返回值

    • 语法:

      def map(self , f: T -> U ) -> RDD[U]
  • flatMap算子

    • 功能:将两层嵌套集合中的每个元素取出,扁平化处理,放入一层集合中返回,类似于SQL中explode函数

    • 分类:转换算子

    • 场景:多层集合元素展开,一个集合对应多个元素【一对多】

    • 语法:

       # 结构:def 函数名(参数) -> 返回值
       # f:这个参数是一个函数function:lambda 参数T: 返回值U
       # T:f这个函数中传递的参数
       # U:f这个函数中返回的结果
       # RDD[U]:最终这个函数返回的是一个RDD,RDD中的元素就是U
       # Iterable:可以理解为列表
       def flatMap(self , f : T -> Iterable[U]) -> RDD[U]
  • filter算子

    • 功能:对RDD集合中的每个元素调用一次参数中的表达式对数据进行过滤,符合条件就保留,不符合就过滤

    • 分类:转换算子

    • 场景:行的过滤,类似于SQL中where或者having

    • 语法:

      def filter(self, f: T -> bool ) -> RDD[T]

3.3 常用触发算子

  • count算子
    • 功能:统计RDD集合中元素的个数,返回一个int值

    • 分类:触发算子

    • 场景:统计RDD的数据量,计算行数

    • 语法:
      def count(self) -> int
  • foreach算子
    • 功能:对RDD中每个元素调用一次参数中的函数,没有返回值【与map场景上区别】

    • 分类:触发算子

    • 场景:对RDD中的每个元素进行输出或者保存,一般用于测试打印或者保存数据到第三方系统【数据库等】

    • 语法:

      def foreach(self , f : T -> None) -> None
  • saveAsTextFile算子
    • 功能:用于将RDD的数据保存到外部文件系统中

    • 分类:触发算子

    • 场景:保存RDD的计算的结果,一般用于将结果保存到HDFS

      • 文件个数 = Task个数 = 分区个数

    • 语法:

      def saveAsTextFile(self , path ) -> None

3.4 其他触发算子

  • first算子
    • 功能:返回RDD集合中的第一个元素 [RDD中有多个分区,返回的是第一个分区的第一个元素]

    • 分类:触发算子

    • 语法:

      def first(self) -> T
  • take算子
    • 功能:返回RDD集合中的前N个元素 [先从第一个分区取,如果不够再从第二个分区取]

    • 分类:触发算子

    • 注意:take返回的结果放入Driver内存中的,take数据量不能过大

    • 语法:

      def take(self , num:int ) -> List[T]
  • collect算子

    • 功能:将RDD转化成一个列表返回

    • 分类:触发算子

    • 注意:这个RDD的数据一定不能过大,如果RDD数据量很大,导致Driver内存溢出

    • 语法:

      def collect(self) -> List[T]
  • reduce算子
    • 功能:将RDD中的每个元素按照给定的聚合函数进行聚合,返回聚合的结果

    • 分类:触发算子

    • 语法:

      # tmp用于存储每次计算临时结果,item就是RDD中的每个元素
      def reduce(self,f : (T,T) -> U) -> U
      
      reduceByKey(lambda tmp,item: tmp+item)

3.5 其他转换算子

  • union算子
    • 功能:实现两个RDD中数据的合并

    • 分类:转换算子

    • 语法:

      def union(self,other:RDD[U]) -> RDD[T/U]
  • distinct算子
    • 功能:实现对RDD元素的去重

    • 分类:转换算子

    • 语法:

      def distinct(self) -> RDD[T]

3.6 分组聚合算子

  • 分类:xxxByKey算子,只有KV类型的RDD才能调用

  • groupByKey算子

    • 功能:对KV类型的RDD按照Key进行分组,相同K的Value放入一个集合列表中,返回一个新的RDD

       RDD[K, V].groupByKey => RDD[K, List[V]]
    • 要求:只有KV类型的RDD才能调用

    • 分类:转换算子

    • 场景:需要对数据进行分组的场景,或者说分组以后的聚合逻辑比较复杂,不适合用reduce

    • 特点:必须经过Shuffle,可以指定新的RDD分区个数,可以指定分区规则

    • 语法:

       def groupByKey(self, numpartitions, partitionFunction) -> RDD[Tuple[K,Iterable[V]]]
  • reduceByKey算子

    • 功能:对KV类型的RDD按照Key进行分组,并对相同Key的所有Value使用参数中的函数进行聚合

    • 要求:只有KV类型的RDD才能调用

    • 分类:转换算子

    • 场景:需要对数据进行分组并且聚合的场景

    • 特点:必须经过shuffle,可以执行新的RDD分区个数,可以指定分区规则

    • 语法:

       def reduceByKey(self,f: (T,T) -> T,numPartitions,partitionFunction) -> RDD[Tuple[K,V]]

3.7 排序算子

  • sortBy算子

    • 功能:对RDD中的所有元素进行整体排序,可以指定排序规则【按照谁排序,升序或者降序】

    • 分类:转换算子

    • 场景:适用于所有对数据排序的场景,一般用于对大数据量非KV类型的RDD的数据排序

    • 特点:经过Shuffle,可以指定排序后新RDD的分区个数,底层只能使用RangePartitioner来实现

    • 语法:

       def sortBy(self, keyFunc:(T) -> 0, asc: Bool, numPartitions) -> RDD
      • keyFunc:(T) -> 0:用于指定按照数据中的哪个值进行排序

      • asc: Boolean:用于指定升序还是降序,默认是升序

  • sortByKey算子

    • 功能:对RDD中的所有元素按照Key进行整体排序,可以指定排序规则

    • 要求:只有KV类型的RDD才能调用

    • 分类:转换算子【sortByKey会触发job的运行】

    • 场景:适用于大数据量KV类型的RDD按照Key排序的场景

    • 特点:经过Shuffle,可以指定排序后新RDD的分区个数

    • 语法:

       def sortByKey(self, asc, numPartitions) -> RR[Tuple[K,V]]

3.8 TopN算子

  • top算子

    • 功能:对RDD中的所有元素降序排序,并返回前N个元素,即返回RDD中最大的前N个元数据

    • 分类:触发算子

    • 场景:取RDD数据中的最大的TopN个元素

    • 特点:不经过Shuffle,将所有元素放入Driver内存中排序,性能更好,只能适合处理小数据量

    • 语法:

      def top(self,num) -> List[0]
  • takeOrdered算子

    • 功能:对RDD中的所有元素升序排序,并返回前N个元素,即返回RDD中最小的前N个元数据

    • 分类:触发算子

    • 场景:取RDD数据中的最小的TopN个元素

    • 特点:不经过Shuffle,将所有元素放入Driver内存中排序,只能适合处理小数据量

    • 语法:

       def takeOrdered(self,num) -> List[0]

3.9 重分区算子

  • repartition算子

    • 功能:调整RDD的分区个数

    • 分类:转换算子

    • 场景:一般用于调大分区个数,必须经过shuffle才能实现

    • 特点:必须经过Shuffle过程,repartition底层就是coalesce(shuffle=True)

    • 语法:

       def repartition(self,numPartitions) -> RDD[T]
  • coalesce算子

    • 功能:调整RDD的分区个数

    • 分类:转换算子

    • 场景:一般用于降低分区个数,不需要经过shuffle就可以实现

    • 特点:可以选择是否经过Shuffle,默认情况下不经过shuffle

    • 语法:

       def coalesce(self, numPartitions, shuffle:boolean) -> RDD[T]
       

4. Spark容错机制

4.1 Spark中RDD的数据如何保证数据的安全

  • 每个RDD在构建数据时,会根据自己来源一步步倒推到数据来源,然后再一步步开始构建RDD数据

  • 当RDD的数据被触发调用时,就会根据RDD的血缘关系层层构建RDD的数据

  • 如果在计算过程中,RDD的数据丢失,就会通过依赖关系重新构建,彻底保证了RDD的数据安全

4.2 persist缓存机制

  • cache

    • 功能:将RDD缓存在内存中

    • 语法:cache()

    • 本质:底层调用的还是persist(StorageLevel.MEMORY_ONLY),但是只缓存在内存,如果内存不够,缓存会失败

    • 场景:资源充足,需要将RDD仅缓存在内存中

  • persist

    • 功能:将RDD【包含这个RDD的依赖关系】进行缓存,可以自己指定缓存的级别【和cache区别】

    • 语法:persist(StorageLevel)

    • 级别:StorageLevel决定了缓存位置和缓存几份

    • 推荐:实际工作中一般推荐使用以下两种

    • 场景:根据资源情况,将RDD缓存在不同的地方或者缓存多份

  • unpersist

    • 功能:将缓存的RDD进行释放

    • 语法:unpersist

      • unpersist(blocking=True):等释放完再继续下一步

    • ‘场景:明确RDD已经不再使用,将RDD的数据从缓存中释放,避免占用资源

    • 注意:如果不释放,这个Spark程序结束,也会释放这个程序中的所有内存

4.3 checkpoint检查点机制

  • 问题: 如果一个RDD是经过非常复杂的转换得到的,可能只使用1次,如果RDD构建过程中数据丢失,只能依赖血脉重新构建,性能会受到影响?

    • 检查点机制: checkpoint

      • 自动触发一个job,构建RDD的数据存储在HDFS

    • 设计目标: 更侧重于安全性

4.4 RDD的cache,persist和checkpoint有什么区别?

  • cache和persist本质上都是persist缓存,cache调用的就是persist,但是不能指定缓存级别,persist允许指定缓存级别

  • persist和checkpoint的区别

存储位置: persist可以选择将数据缓存在内存或者磁盘,checkpoint将数据存储在磁盘
生命周期: persist缓存的数据当遇到unpersist或者程序结束就被释放了,chk的数据必须手动删除
存储内容: persist存储的是RDD,保留RDD的血脉关系,chk只会存储RDD的数据,不包含血脉关系

5. Spark高级特性

5.1 Broadcast Variables广播变量

  • 功能: 将一个变量元素进行广播到每台Worker节点的Executor中,让每个Task直接从本地读取数据,减少网络传输IO

  • 语法

    • step1:将一个变量定义成为一个广播变量

    • step2:当需要用到这个变量时,就从广播变量中获取它的值

    • step3:释放广播变量

       broadcast_city_dict.unpersist()
  • 场景

    • a. 广播一个较大的数据,减少每次从Driver复制的数据量,降低网络IO损耗,提高性能

    • b. 两张表进行Join时,将小表进行广播,与大表的每个部分进行Join,实现Broadcast Join, 避免Shuffle Join【类似于map join】

  • 特点广播变量是一个只读变量,不能修改

5.2 Spark应用中的概念

  • 问题:ClusterManager、Worker、Application、Driver、Executor、Job、Stage、Task分别是什么?

    • 架构层面:ClusterManager、Worker

    • 程序层面:Application、Driver、Executor

    • 运行层面:job、DAG图【Stage】、Task

  • Cluster Manager:分布式资源管理的主节点

    • 分布式资源管理平台主节点设计

      • Standalone: Master

      • YARN: ResourceManager

    • 功能: 管理节点

      • 管理从节点

      • 实现资源管理和任务调度

      • 接收客户端请求

  • Worker:分布式资源管理的从节点

    • 分布式资源管理平台从节点

      • Standalong: Worker

      • YARN: NodeManager

    • 功能: 利用自己节点的资源进行主节点分配的任务

  • Application:Spark的应用程序

    • 开发者基于Spark API开发好的Spark的程序

    • 任何一个Spark程序都包含一个Driver进程和至少一个Executor进程

    • 不同语言: 产出的程序文件

      • Python: Python文件

      • Java/Scala: jar包

  • Driver:Spark程序的驱动进程

    • 每个Spark程序都会包含一个Driver进程,负责初始化工作

    • 功能:

      • 负责申请启动Executor进程

      • 负责解析代码构建Task,调度分配给Executor运行,监控Task运行

  • Executor:Spark程序的执行进程

    • 每个Spark程序在集群模式都拥有自己的Executor

    • 运行: 利用Worker的资源运行在Worker节点上

    • 功能: 负责运行Driver分配的Task任务

  • Job

    • Spark程序触发Task的最小单元: 要想构建Task运行, 必须先触发job

    • job触发由触发算子决定,Driver会解析代码,一旦遇到了触发算子,就会构建job

    • Driver会通过DAGScheduler组件使用回溯算法【倒推】为每个job构建DAG图【执行计划图】

  • Stage

    • 执行阶段: 每个job触发以后会构建DAG图,而构建DAG图的过程中会划分Stage

    • 划分规则: 根据是否经过Shuffle或者宽依赖来划分

    • 一个Stage内部的所有转换过程都是直接在内存中完成

    • Stage划分以后按照整个程序全局编号,执行的时候,按照编号从小到大开始执行

  • Task

    • 每个Stage【逻辑计划】最终都会转换一个TaskSet集合

    • 一个TaskSet集合中可以包含多个Task,每个分区会对应一个Task进行处理

    • Driver会通过TaskScheduler将TaskSet中的Task分配Executor来运行

    • 每个Task【物理计划】在Executor中会使用1CoreCPU来运行这个Task

5.3 一个Spark程序是如何运行的

  • step0: 先启动分布式资源管理的集群:Spark Standalone / YARN

  • step1: 客户端提交用户开发好的Spark Application程序给ClusterManager

  • step2: ClusterManager根据配置参数运行程序,启动Driver进程

  • step3: Driver进程向主节点提交申请启动Executor进程

  • step4: 主节点根据资源配置和请求,在从节点上启动Executor进程

  • step5: 所有Executor启动成功以后,会向Driver反向注册,等待分配Task

  • step6: Driver解析代码,知道遇到触发算子,开始触发job运行

  • step7: Driver会调用DAGScheduler组件为当前这个job通过回溯算法构建DAG图,并划分Stage

  • step8:Driver会将这个job中每个Stage转换为TaskSet,根据Stage中最后一个RDD分区数来构建Task个数

  • step9:Driver调用TaskManager将Task调度分配到Executor中运行

5.4 Spark的宽窄依赖

  • 什么是依赖关系?

    • RDD会不断进行转换处理,得到新的RDD,每个RDD之间就产生了依赖关系

  • 窄依赖

    • 定义: 父RDD的一个分区的数据只给了子RDD的一个分区[不用调用分区器]

    • 特点: 一对一或一对多,不经过Shuffle,性能向对较快,但无法实现全局分区,排序,分组等

    • 一个Stage内部的计算都是窄依赖的过程,全部在内存中完成

  • 宽依赖

    • 定义: 父RDD的一个分区的数据给了子RDD的多个分区【需要调用Shuffle的分区器来实现】

    • 特点: 一对多,必须经过Shuffle,性能相对较慢,可以实现全局分区、排序、分组等

    • Spark的job中按照宽依赖来划分Stage

  • 本质: 只是一种标记,标记两个RDD之间的依赖关系


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值