Spark原理与实践 | 青训营笔记
这是我参与「第四届青训营 」笔记创作活动的的第5天
今天笔记主要分为四个部分:
大数据处理引擎Spark介绍
Spark生态和特点
特点:
- 统一引擎,支持多种分布式场景
- 多语言支持
- 可读写丰富数据源
- 丰富灵活的API/算子
- 支持K8S、YERN、MESOS资源调度
运行架构
首先用户创建一个SparkContext,SparkContext链接到Cluster Manager,Cluster Manager会根据用户提交设置的参数(CPU,内存)去给用户分配资源,启动Executor。Driver会将用户程序划分为不同的stage,每个stage会有完全相同的一组task,这些task会作用于待处理的数据的不同分区,在阶段划分完成后和task创建完成后,Driver会向Executor发送task,Executor在接受task之后,会下载依赖,准备好执行环境。并将每次运行状态发送给Driver,Driver根据运行状态做状态更新,直到所有task执行正确或超出执行限制。
部署方式
- Spark Local Mode 本地测试/单进程多线程模式
- Spark Standalone Mode 需要启动Spark的Standalone集群的Master/Worker
- On YARN/K8S 依赖外部资源调度器
SparkCore 原理解析
RDD
定义
表示可并行操作的不可变的分区元素集合。
RDD五要素
RDD算子
- Transform算子:生成一个新的RDD
- Action算子:触发Job提交
RDD依赖
- 窄依赖:父RDD的每个partition至多对应一个子RDD分区
- NarrowDependency
- PruneDependency
- RangeDependency
- OneToOneDependency
- 宽依赖:父RDD的每个partition都可能对应多个子RDD分区
RDD执行流程
调度器
内存管理
执行器内存有两种:Storage、Execution
- UnifiedMemoryManager统一管理Storage/Execution内存
- Storage、Execution内存使用的是动态调整,可以互相借用。
- 当Storage空闲 ,Execution可以借用Storage的内存使用
- 可以减少spill等操作,Execution使用的内存不能被Storage驱逐
- 当Execution空闲,Storage可以借用Execution的内存使用
- 当Execution需要内存时,可以驱逐被Storage借用的内存,直到spark.memory.storageFraction边界
多任务间内存分配
UnifiedMemoryManager统一管理Storage/Execution内存
每个Task获取的内存区间为1/(2*N)~1/N (N为当前执行器中正在并发运行的task数量)
Shuffle
SortShuffleManager
External Shuffle Service
shuffle write 的文件被Node Manager中的Shuffle Service托管,供后续Reduce Task进行shuffle fetch,如果Executor空闲,DRA可以进行回收。
SparkSQL原理解析
SparkSQL
Catalyst优化器
RBO
Batch执行策略:
- Once ->只执行一次
- FixedPoint -> 重复执行,直到plan不再改变,或者执行达到固定次数(默认100次)
RBO规则:
transformDown 先序遍历树进行规则匹配
transformUp 后序遍历树进行规则匹配
CBO
需要预先统计数据
AQE(自适应查询)
每个Task结束会发送MapStatus信息给Driver
特点:边优化边执行,根据已经完成计划的真实节点去做统计,进行反馈,优化剩余计划。
三种优化场景
Partition合并
作业运行过程中,根据前面运行完的Stage的MapStatus中实际的partition大小信息,可以将多个相邻的较小的partition进行动态合并,右一个Task读取进行处理。
动态切换JOIN策略
1存在的问题时Catalyst Optimizer优化阶段,算子的statistics估算不准确,生成的执行计划并不是最优。
2的解决方案时AQE运行过程中动态获取准确JOIN的leftChild/rightChild的实际大小,将SMJ转换为BHJ。
Skew Join
AQE根据MapStates信息自动检测是否右倾斜,将大的partition拆分成多个Task进行Join
Runtime Filter
Runtime Filter减少了大表的扫描,shuffle的数据量以及参加啊Join的数据量,所以对整个集群IO/网络/CPU有比较大的节省
Bloom Runtime Filter
Codegen
Expression(表达式)
将表达式中的大量虚函数调用压平到一个函数内部,类似手写代码。
WholeStageCodegen
原先编译的火山模型算子之间大量的虚函数调用,开销大
现在将同一个Stage中的多个算子压平到一个函数内部去执行。
业界挑战与实践
Shuffle 稳定性问题
在大规模作业下,开源ESS的实现机制容易带来大量随机读导致的磁盘IOPS瓶颈、Fetch请求积压等问题,进而导致运算过程中经常会出现Stage重算甚至作业失败,继而引起资源使用的恶性循环,严重影响SLA。
解决方案(下次笔记重点写shuffle)
- 数据远端存储
- Map,Reduce端partition的数据进行聚合
SQL执行性能问题
目前的解决方案:
参数推荐/作业诊断
存在的问题:
- 调参难度大
- 参数不合理的作业,对资源利用率/Shuffle稳定性/性能有非常大影响
- 线上作业失败/运行慢的时候用户排查难度大
解决方案:
- 自动参数推荐
- 作业诊断