Spark 原理与实践-复习笔记
这是我参与「第四届青训营 」笔记创作活动的的第6天!
1. 大数据处理引擎Spark介绍
1.1 大数据技术栈
- 应用————Bl报表/实时大盘/广告/推荐
- 计算————Spark/Flink/Presto/lmpala/ClickHousel... YARN/K8S
- 存储————MetaStore Parquet/ORC/DeltaLake/Hudi/Iceberg... HDFS/Kafka/HBase/Kudu/TOS/S3/...
- 数据————Velume/VarietyVelocity On-Premise/On-Cloud 平台/管理/安全/...
1.2 常见大数据处理链路
数据源——>数据采集——>分布式数据库(原始数据)——>数据处理(多次读写等操作)——>数据计算——>数据应用
1.3 开源大数据处理引擎
-
Batch
hadoop Map Reduce、Spark -
Streaming
Flink -
OLAP
presto、ClickHouse、Impala、DORIS
1.4 什么是Spark
- Apache Spark is a multi-language engine for executing data engineering, data science, and machine learning on single-node machines or clusters.
- Key features
- Batch/streaming
- data Unify the processing of your data inbatches and real-time streamingusing your prefered language:Python sQL Scala,Java or R.
- SQL analytics
- Execute fast, distributed ANSI SQL queries for dashboarding and ad-hoc reporting. Runs faster than most data warchouses.
- Data science at scale
- Perform Exploratory Data Analysis (EDA)on petabyte-scale data without having to resort to downsampling
- Machine learning
- Train machine learning algorithms on a laptop and use the same code to scale to fault-tolerant clusters of thousands of machines.
1.5 Spark生态组件
- Spark Core:Spark 核心组件,它实现了Spark的基本功能,包含任务调度、内存管理、错误恢复、与存储系统交互等模块。
- Spark SQL:用来操作结构化数据的核心组件,通过 Spark SQL 可以直接查询 Hive、HBase 等多种外部数据源中的数据。
- Spark Structured Streaming:Spark 提供的流式计算框架,支持高吞吐量、可容错处理的实时流式数据处理。
- MLlib:Spark 提供的关于机器学习功能的算法程序库,包括分类、回归、聚类、协同过滤算法等,还提供了模型评估、数据导入等额外的功能。
- GraphX:Spark 提供的分布式图处理框架,拥有对图计算和图挖掘算法的API接口以及丰富的功能和运算符。
- 独立调度器、Yarn、Mesos、Kubernetes:Spark 框架可以高效地在一个到数千个节点之间伸缩计算,集群管理器则主要负责各个节点的资源管理工作,为了实现这样的要求,同时获得最大灵活性,Spark 支持在各种集群管理器(Cluster Manager)上运行。
1.6 Spark 运行架构和工作原理
- Application(应用):Spark上运行的应用。Application中包含了一个驱动器(Driver)进程和集群上的多个执行器(Executor)进程。
- Driver Program(驱动器):运行main0方法并创建SparkContext的进程。
- Cluster Manager(集群管理器):用于在集群上申请资源的外部服务(如:独立部署的集群管理器、Mesos或者Yarn)。
- Worker Node(工作节点):集群上运行应用程序代码的任意一个节点。
- Executor(执行器):在集群工作节点上为某个应用启动的工作进程,该进程负责运行计算任务,并为应用程序存储数据。
- Task(任务):执行器的工作单元。
- Job(作业):一个并行计算作业,由一组任务(Task)组成,并由Spark的行动(Action)算子(如:save、collect)触发启动。
- Stage(阶段):每个Job可以划分为更小的Task集合,每组任务被称为Stage。
1.7 Spark目前支持几个集群管理器
- Standalone:Spark 附带的简单集群管理器,可以轻松设置集群。
- Apache Mesos:通用集群管理器,也可以运行Hadoop MapReduce 和服务应用程序。(已弃用)
- Hadoop YARN:Hadoop2和3中的资源管理器。
- Kubernetes:用于自动部署、扩展和管理容器化应用程序的开源系统。
2. SparkCore 原理解析
2.1 SparkCore
- RDD(Resilient Distributed Dataset):弹性分布式数据集,是一个容错的、并行的数据结构
- RDD算子:对任何函数进行某一项操作都可以认为是一个算子,RDD算子是RDD的成员函数
- Transform(转换)算子:根据已有RDD创建新的RDD Action(动作)算子:将在数据集上运行计算后的数值返回到驱动程序,从而触发真正的计算
- DAG(Directed Acyclic Graph):有向无环图,Spark中的RDD通过一系列的转换算子操作和行动算子操作形成了一个DAG
- DAGScheduler:将作业的DAG划分成不同的Stage,每个Stage都是TaskSet任务集合,并以TaskSet为单位提交给TaskScheduler。
- TaskScheduler:通过TaskSetManager管理Task,并通过集群中的资源管理器(Standalone模式下是Master,Yarn模式下是ResourceManager)把Task发给集群中Worker的Executor
- Shuffle:Spark中数据重分发的一种机制。
2.2 RDD(Resilient Distributed Dataset)
2.2.1 什么是RDD\
-
RDD(Resilient Distributed Dataset):弹性分布式数据集,是一个容错的、并行的数据结构。
-
Represents an immutable, partitioned collection of elements that can be operated on in parallel.
-
描述RDD的五要素
| Partitions | A list of partitions |
|---|---|
| Compute | A function for computing each split |
| Dependencies | A list of dependencies on other RDDs |
| Partitioner | a Partitioner for key-value RDDs(e.g.to say that the RDD is hash-paaetitioned) |
| PreferredLocations | a list of preferred locations to compute each split on(e.g.block locations for an HDFS file) |
2.2.2 如何创建RDD
- 内置RDD
ShuffleRDD/HadoopRDD/JDBCRDD/KafikaRDD/UnionRDD/MapPartitionsRDD/.. - 自定义RDD
class CustomRDD(.)extends RDD}
实现五要素对应的函数
2.2.3 RDD算子
-
Transform 算子:生成一个新的RDD
map/filter/flatMap/groupByKey/reduceByKey/... -
Action 算子:触发Job提交 collect/count/take/saveAs TextFile/…
2.2.4 RDD依赖
RDD依赖:描述父子RDD之间的依赖关系(lineage)。
- 窄依赖:父RDD的每个partition至多对应一个子RDD分区。
- NarrowDependency
def getParents(partitionld:Int):Seq[lnt] - One ToOneDependency
overide def getParents(partitionldInt):List[lnt]=List(partitionld) - RangeDependency
overide def getParents(partitionld:Int):List[lnt]= if(partitionld >=outStart&&partitionld <outStart+length) List(partitionld-outStart +inStart) - PruneDependency
- NarrowDependency
- 宽依赖:父RDD的每个parttion都可能对应多个子RDD分区。
- ShufleDependency
2.2.5 RDD执行流程
Job:RDD action 算子触发
Stage:依据宽依赖划分
Task:Stage内执行单个partition任务
2.3 Scheduler
根据ShufleDependency 切分Stage,并按照依赖顺序调度Stage,为每个Stage生成并提交TaskSet到TaskScheduler
根据调度算法(FIFO/FAIR)对多个TaskSet进行调度,对于调度到的TaskSet,会将Task 调度(locality)到相关Executor上面执行,Executor SchedulerBackend提供
2.4 Memory Management
- Executor 内存主要有两类:Storage、Execution
- UnifiedMemoryManager 统一管理 Storage/Execution内存
- Storage和Execution内存使用是动态调整,可以相互借用
- 当Storage 空闲,Execution 可以借用Storage的内存使用,可以减少spill等操作,Execution使用的内存不能被 Storage驱逐
- 当Execution空闲,Storage 可以借用Execution的内存使用,当Execution 需要内存时,可以驱逐被 Storage借用的内存,直到spark.memory.storageFraction 边界
- UnifiedMemoryManager 统一管理多个并发Task的内存分配
- 每个Task获取的内存区间为1/(2*N)~1/N,N为当前Executor中正在并发运行的task数量
2.5 Shuffle
spark shuffle.manager->sort
trait ShufileManager{
def registerShuffle...
def getWriter.…
def getReader..…
def unregisterShuffle...
}
class SortShuffleManager extends ShuffleManager
每个MapTask生成一个Shufle数据文件和一个index文件dataFile 中的数据按照partitionld进行排序,同一个partitionld的数据聚集在一起indexFile 保存了所有paritionld在dataFlle中的位置信息,方便后续ReduceTask 能Fetch 到对应 partitionld的数据
3. SparkSQL 原理解析
3.1 Catalyst 优化器
3.1.1 Catalyst 优化器-RBO
Batch执行策略:
Once->只执行一次
FixedPoint>重复执行,直到plan不再改变,或者执行达到固定次数(默认100次)
private val defaultBatches=Sea(
Batch("Propagate Eapty ReLations",fixedpoint,
AQEPropagateEnptyRelation, ConvertTolocatRelation,
UpdateAttributelullability),
Batch("Dynanic Join Selection",once,Dynanic2einselection),
Batch("Eliminate Linits",fixedPoint,EliminateLinits),
Batch("Optinize one Row Plan",fixedpoint,Optiaizeonetowplan)):+
Batch("User Provided Runtine Optinizers",
fixedPoint,extendedRuntineOptinizerRules:_*)
transformDown 先序遍历树进行规则匹配
transformUp 后序遍历树进行规则匹配
3.2 Adaptive Query Execution(AQE)
- 每个Task结束会发送MapStatus信息给Driver
- Task的MapStatus中包含当前Task Shufle产生的每个Partition的size统计信息
- Driver获取到执行完的Stages的MapStatus信息之后,按照MapStatus中partition大小信息识别匹配一些优化场景,然后对后续未执行的Plan进行优化
目前支持的优化场景:
- Partiton合并,优化shuffle读取,减少reduce task个数
- SMJ->BHJ
- Skew Join优化
3.3 Runtime Filter
Runtime Filter减少了大表的扫描,shufle的数据量以及参加Join的数据量,所以对整个集群IO/网络/CPU有比较大的节省(10TB TPC-DS获得了大约35%的改进)
3.4 Bloom Runtime Filter
3.5 Codegen
3.5.1 Codegen-Expression
- 将表达式中的大量虚函数调用压平到一个函数内部,类似手写代码
- 动态生成代码,Janino即时编译执行
3.5.2 Codegen-WholeStageCodegen
一个SQL包含多个Stage的WholeStageCodegen
4. 业界挑战与实践
4.1 Shuffle 稳定性问题
在大规模作业下,开源ExternalShufleService(ESS)的实现机制容易带来大量随机读导致的磁盘IOPS瓶颈、Fetch请求积压等问题,进而导致运算过程中经常会出现Stage重算甚至作业失败,继而引起资源使用的恶性循环,严重影响 SLA.
4.2 SQL 执行性能问题
- 压榨CPU资源 CPU流水线/分支预测/乱序执行/SIMD/CPU缓存友好/…
4.3 参数推荐/作业诊断
- Spark参数很多,资源类/Shuffle/Join/Agg/... 调参难度大
- 参数不合理的作业,对资源利用率/Shuffle稳定性/性能有非常大影响
- 同时,线上作业失败/运行慢,用户排查难度大
解决方式:自动参数推荐/作业诊断