Spark原理| 青训营笔记

130 阅读6分钟

这是我参与「第四届青训营 」笔记创作活动的第4天

 

1. 大数据处理引擎

2. SparkCore原理解析

3. SparkSQL原理解析

 

 

SparkCore:实现基本功能,任务调度、基本管理、错误恢复、存储、系统交互

SparkSQL:操作结构数据,交互查询数据源

MLlib:Spark提供的关于机器学习的算法数据库,分类回归聚类、模型评估、数据导入导出

GraphX:分布式图处理框架

 

Spark支持多种语言:SQL、java/scala、R、python

Spark支持多种数据源:Text、Parquet/ORC、JSON/CSV、JDBC

自定义DataSource,实现Datasource API对接HBase/Mongo.....  spark-packages.org

 

Spark-丰富的API/算子

SparkCore→RDD弹性分布式数据集

SparkSQL→Dataframe

 

 

 

1 Spark运行架构&部署方式

Spark运行架构.png

 

Spark运行架构

提交-计算的过程:

1. 用户创建Sparkcontext,sparkcontext连接到cluster manager

2. Cluster manager分配计算资源,启动executor

3. Driver会把用户程序分到不同stage,而stage会由有一组完全相同的task组成

4. Driver向executor发送task

5. Executor下载task的运行依赖、准备执行环境

6. 应用实时将task的状态发给driver,driver状态更新并把task发到executor执行

7. 直到所有task执行完成

 

部署方式

1. Spark local mode 主要用于本地测试/单进程多线程模式

   Spark-sql --master local[*]...

2. Spark standalone mode需要启动spark的standalone集群的master/worker,不需要外部的资源调度。

   Spark-sql --master spark://masterip:{master_ip}:{port}...

3. 依赖外部资源调度(YARN/K8S)

   Spark-sql --master yarn ...

   Spark-sql --master k8s://

 

2 SparkCore

 

2.1  Sparkcore总体

sparkcore总体.png

2.2  RDD

resilient distributed dataset:弹性分布式数据集,是Spark中最基本的数据抽象,代表一个不可变、可分区、元素可并行计算的集合。

 

RDD五要素

RDD五要素.png

Partition:

RDD是一组Partition的列表(RDD内部的数据集在逻辑和物理上划分为多个partitions)。Spark中任务是以task线程的方式运行,一个Partition对应一个task线程。

 

Compute:

Spark中RDD的计算以Partition为单位,每个 RDD 都会实现compute()函数以达到这个目的。compute()函数会对迭代器进行复合,不需要保存每次计算的结果。

 

Dependencies:

RDD之间存在依赖关系。

由于RDD是只读的数据集,如果对RDD中的数据进行改动,就只能通过 Transformation 操作,由一个或多个RDD计算生成一个新的RDD,所以RDD之间就会形成类似 Pipeline的前后依赖关系,前面的称为parent RDD,后面的称为child RDD。当计算过程中出现异常情况导致部分 Partition 数据丢失时,Spark 可以通过这种依赖关系从父 RDD 中重新计算丢失的分区数据,而不需要对 RDD 中的所有分区全部重新计算,以提高迭代计算性能。

 

Partitioner:

两种类型的分区函数,一种是基于哈希的,另一种基于范围。只有对于Key-Value型的RDD才会有Partitioner,非Key-Value型的RDD的Partitioner值是None。

 

Preferred location:

“优先位置”,存储每个partition的一个优先位置的列表。

对于每个 HDFS 文件来说,这个列表保存的就是每个Partition所在block的位置。按照“移动数据不如移动计算”的理念,Spark 在进行任务调度的时候,会尽可能地优先将计算任务分配到其所要处理的数据块的存储位置,减少数据的网络传输,提升计算效率。

 

RDD算子(两类)

Transform算子:生成新的RDD

Action算子:触发job提交

 

RDD依赖:窄依赖和宽依赖

描述父子RDD之间的依赖关系。

窄依赖:父RDD的每个partition至多对应一个子RDD分区。

宽依赖:父RDD的每个partition都可能对应多个子RDD分区。

RDD依赖.png

 

 

RDD执行流程

RDD执行流程.png

 

Stage:当spark执行作业时,会根据RDD之间的宽窄依赖关系,将DAG划分成多个相互依赖的stage。

Stage划分方式:从后往前推。如果遇到RDD之间为窄依赖,由于Partition依赖关系的确定性,Transformation操作可以在同一个线程里完成,窄依赖就被划分到同一个Stage中;如果遇到 RDD 之间为宽依赖(shuffledependency),则划分到一个新的Stage中,且新的Stage为之前Stage的 Parent,然后依次类推递归执行,Child Stage 需要等待所有的 Parent Stages 执行完成后才可以执行。

这样每个 Stage 内的 RDD 都尽可能在各个节点上并行地被执行,以提高运行效率。  

最后一个RDD的partition的数量决定了最后task的数量。

2.3  Scheduler调度器

1.RDD创建后,spark会根据RDD对象形成DAG图。

2.触发job以后会将DAG提交给DAGscheduler,根据shuffledependency划分stage,并按照依赖顺序调度stage,为每个stage生成并提交taskset到taskscheduler。

3.根据调度算法(FIFO/FAIR)对多个Taskset进行调度。

4.得到调度的taskset,会将Task调度(locality)到相关Executor上面执行。

scheduler调度器.png  

 

2.4  memory management

-Executor内存主要有两类:Storage/Execution

-unifiedmemorymanager统一管理storage/execution内存

-Storage和execution内存使用是动态调整,可以相互借用

-当storage空闲,Execution可以借用storage的内存使用

-execution使用的内存不能被storage驱逐

-当execution空闲,storage可以借用execution的内存使用

-当execution需要内存时,可以驱逐被storage借用的内存,直到spark.memory.storagefraction变节

memory manager.png

Unifiedmemorymanager统一管理多个并发task的内存分配

每个task获取的内存区间为1/(2*N)~1/N,N为当前Executor中正在并发运行的task数量。

2.5  shuffle

Map和reduce之间数据重新分发的过程称为shuffle。

shuffle.png

每个maptask生成一个shuffle数据文件和index索引文件。

 

详见shuffle章节的学习。

 

3  SparkSQL原理解析

sparksql总体.png

SQL Query转化成RDD的过程:

SQL转化RDD.png

 

3.1  Catalyst优化器

catalyst优化器.png

参照RBO/CBO内容

3.2 Adaptive Query Execution(AQE)自适应查询

根据运行时的统计数据,自动对正在运行的任务进行优化。边执行边优化,提高了SQL的执行效率。

Stage把它的task信息传给driver。(每个Task结束会发送MapStatus信息给DRIVER)

 

Coalescing shuffle partitions(partition合并)

partition合并.png Stage实际处理的数据不一样,可能某些stage的性能比较差。

如:

-partition参数对某个stage过大,则可能单个partition的大小比较小,且task个数会比较多,shuffle fetch阶段产生大量的小块随机读,影响性能

-partition参数对某个stage过小,则可能单个partition的大小比较大,会产生更多的spill或者OOM

作业运行中,根据前面运行完的stage的mapstatus中实际的partition大小信息,可以将多个相邻的较小的partition进行动态合并,由一个Task读取进行处理。

 

Catalyst Optimizer优化阶段,算子的statistics估算不准确,生成的执行计划并不是最优。

AQE运行过程中动态获取准确join的leftChild/rightChild的实际大小,将SMJ(SortMergeJoin)转换为BHJ(BroadcastHashJoin)。

 

对于数据倾斜问题。AQE会根据shuffle的统计来自动检测是否有倾斜,江大的partition茶分成多个task进行join。

倾斜.png

 

3.3  Runtime filter

对join算法的优化

Runtime Filter减少了达标的扫描,shuffle的数据量以及参加join的数据量,所以对整个集群IO/网络/CPU有比较大的节省。

 

附:Bloom Runtime Filter

 

3.4 Codegen - Expression

 

挑战和问题

Spark参数很多,资源类/Shuffle/Join/Agg/...调参难度大

参数不合理的作业,对资源利用率/Shuffle稳定性/性能有很大影响

线上作业失败/运行慢,用户排查难度大。