这是我参与「第四届青训营」笔记创作活动的的第5天
Spark简介
- 大数据处理链路:数据源(数据库、日志),通过数据采集存放到HDFS、kafka等,再进行数据处理(批处理、流处理、OLAP by clickhouse/presto/impala等),最后数据应用(如BI报表,OLAP、实时、机器学习等)
SparkCore原理
RDD
- RDD:弹性分布式数据集,是一个容错的、并行的数据结构
- RDD算子:对任何函数进行某一项操作都可以认为是一个算子,RDD算子是RDD的成员函数
- 划分Stage的整体思路:从后往前推,遇到宽依赖就断开,划分为一个Stage。遇到窄依赖,就将这个RDD加入该Stage中,DAG最后一个阶段会为每个结果的Partition生成一个ResultTask。每个Stage里面的Task数量由最后一个RDD的Partition数量决定,其余的阶段会生成ShuffleMapTask。
- RDD执行流程:当RDD对象创建后,SparkContext会根据RDD对象构建DAG有向无环图,然后将Task提交给DAGScheduler。DAGScheduler根据ShuffleDependency将DAG划分为不同的Stage,为每个Stage生成TaskSet任务集合,并以TaskSet为单位提交给TaskScheduler。TaskScheduler根据调度算法(FIFO/FAIR)对多个TaskSet进行调度,并通过集群中的资源管理器(Standalone模式下是Master,Yarn模式下是ResourceManager)把Task调度(locality)到集群中Worker的Executor,Executor由SchedulerBackend提供。
Scheduler
DAGScheduler:将作业的DAG划分成不同的Stage,每个Stage都是TaskSet任务集合,并以TaskSet为单位提交给TaskScheduler。
内存管理
- Spark采用统一内存管理机制。重点在于动态占用机制。
- 动态占用机制如下:双方的空间都不足时,则存储到硬盘;若己方空间不足而对方空余时,可借用对方的空间。当Storage空闲,Execution可以借用Storage的内存使用,可以减少spill等操作, Execution内存不能被Storage驱逐。Execution内存的空间被Storage内存占用后,可让对方将占用的部分转存到硬盘,然后"归还"借用的空间。当Execution空闲,Storage可以借用Execution内存使用,当Execution需要内存时,可以驱逐被Storage借用的内存,可让对方将占用的部分转存到硬盘,然后"归还"借用的空间
- Executor 内存主要有两类:Storage、Execution,由Unified Memory Manager统一管理,内存使用动态调整,可互相借用。
SparkSQL原理
SparkSQL执行过程
- SQL Parse: 将SparkSQL字符串或DataFrame解析为一个抽象语法树/AST,即Unresolved Logical Plan
- Analysis:遍历整个AST,并对AST上的每个节点进行数据类型的绑定以及函数绑定,然后根据元数据信息Catalog对数据表中的字段进行解析。 利用Catalog信息将Unresolved Logical Plan解析成Analyzed Logical plan
- Logical Optimization:该模块是Catalyst的核心,主要分为RBO和CBO两种优化策略,其中RBO是基于规则优化,CBO是基于代价优化。 利用一些规则将Analyzed Logical plan解析成Optimized Logic plan
- Physical Planning: Logical plan是不能被spark执行的,这个过程是把Logic plan转换为多个Physical plans
- CostModel: 主要根据过去的性能统计数据,选择最佳的物理执行计划(Selected Physical Plan)。
- Code Generation: sql逻辑生成Java字节码
Catalyst优化
RBO/CBO
AQE
可以根据已经完成的计划结点真实且精确的执行统计结果来不停的反馈并重新优化剩下的执行计划。
AQE框架三种优化场景:
- 动态合并shuffle分区(Dynamically coalescing shuffle partitions)
- 动态调整Join策略(Dynamically switching join strategies)
- 动态优化数据倾斜Join(Dynamically optimizing skew joins)
Runtime Filter
实现在Catalyst中。动态获取Filter内容做相关优化,当我们将一张大表和一张小表等值连接时,我们可以从小表侧收集一些统计信息,并在执行join前将其用于大表的扫描,进行分区修剪或数据过滤。可以大大提高性能。
Runtime优化分两类:
- 全局优化:从提升全局资源利用率、消除数据倾斜、降低IO等角度做优化。包括AQE。
- 局部优化:提高某个task的执行效率,主要从提高CPU与内存利用率的角度进行优化。依赖Codegen。
业界挑战
Shuffle稳定性问题
External Shuffle Service的实现机制容易带来大量随机读导致的磁盘IOPS瓶颈、Fetch请求积压等问题,进而导致运算过程中经常出现Stage重算甚至作业失败,继而引起资源使用的恶性循环。 解决方案:见下节课。阿里解决方案:RemoteShuffleService
SQL执行性能问题
压榨CPU资源, 解决方案:Photon、OAP中的gazelle_plugin
参数推荐
Spark参数很多,调参难度大 参数不合理,对资源利用率和Shuffle稳定性影响很大 线上作业失败或运行慢,用户排查难度大。 解决方案:自动参数推荐、作业诊断