Spark原理与实践 | 青训营笔记
这是我参与「第四届青训营 」笔记创作活动的的第5天
Spark简介
Apache Spark是用于大规模数据处理的统一分析引擎。它提供了Java、Scala、Python和R的高级api,以及支持通用执行图的优化引擎。它还支持一系列丰富的高级工具,包括用于SQL和结构化数据处理的Spark SQL,用于pandas工作负载的Spark上的pandas API,用于机器学习的MLlib,用于图形处理的GraphX,以及用于增量计算和流处理的structured Streaming。
Spark原理
- Spark应用作为独立的进程集合运行在集群上,由主程序(称为驱动程序Driver Program)中的SparkContext对象协调。
- 在集群上运行时,SparkContext能连接到多个类型的集群管理器(Spark自己的独立集群管理器,Mesos,YARN或者Kubernets),集群管理器在应用程序之间分配资源。一旦连接成功,Spark需要集群节点中的executors进程为应用程序处理计算和存储数据。接下来发送应用程序的代码给executors。最后SparkContext发送tasks给executors来运行。
- 集群上的任务是由Driver调度的,driver节点应该和worker距离近一些,最好在一个本地局域网中
- 每个应用获得自己的executor进程,并且executor通过多线程运行tasks。利于数据隔离,但也意味着如果不将数据写入外部存储系统,数据就无法在不同的Spark应用程序(SparkContext的实例)之间共享。
CLuster Manager类型
- Standalone:一个简单的独立部署模式,可以手动启动一个master和workers,或者运行启动脚本。
- Apache Mesos:通用的集群管理器,可以运行Hadoop MapReduce和服务应用(不推荐)
- Hadoop YARN:Hadoop 2和3的资源管理器
- Kubernets:一个用于自动化部署、扩展和管理容器化应用程序的开源系统
SparkCore
RDD(Resilient Distributed Dataset):弹性分布式数据集,表示一个不可变的,可以并行操作的分区的元素集合。
特性:
- Partition:Partitions列表,分区列表。RDD的分区数量可以指定,在集群中创建时根据集群中CPU核数,在HDFS中根据文件的BLOCK数创建。
- Compute:Spark RDD中都有一个计算函数,以partition为基本单位
- Dependencies:RDD依赖列表,每个RDD依赖于其他RDD,相互之间有依赖关系,用于数据恢复,减少计算。
- Partitioner:实现两种分区函数,基于hash和基于范围range,基于key-value的RDD才有partitioner,非Key-Value的RDD partitioner为空。
- PreferredLocations:每个分区都有优先位置列表
算子: map、filter、count、cache、persist
创建RDD
- 内置RDD
- 自定义RDD
RDD 算子
- Transform算子:生成新的RDD
- Action算子:触发Job提交
RDD 依赖(这个地方理解不太够)
- 窄依赖:父RDD的每个Partition至多对应一个子RDD分区
- NarrowDependency(找父RDD操作,三种类型如下)
- OneToOneDependency:一对一的获取父RDD。
- RangeDependency:inStart(父RDD起始位置), outStart(子RDD起始位置),Length(Range长度)。获取父RDD的partition的规则:子RDD的partition index在父RDD的range内,返回父RDD。
- PruneDependency:对父RDD的partition做Filter操作。
- NarrowDependency(找父RDD操作,三种类型如下)
- 宽依赖:父RDD的每个partition都可能对应多个子RDD分区。
- ShuffleDependency
RDD执行流程(还是有问题)
划分Stage,stage是啥?
划分Stage的整体思路:从后往前推,遇到宽依赖就断开,划分为一个Stage。遇到窄依赖,就将这个RDD加入该Stage中,DAG最后一个阶段会为每个结果的Partition生成一个ResultTask。每个Stage里面的Task数量由最后一个RDD的Partition数量决定,其余的阶段会生成ShuffleMapTask。
作者:青训营官方账号
链接:juejin.cn/post/712390…
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
调度器
RDD.action->runJob()生成DAG(有向无环图),将DAG提交给DAGScheduler,根据ShuffleDependency切分Stage,并按照依赖顺序调度Stage,为每个Stage生成并提交TaskSet到TaskScheduler,根据调度算法进行调度到executors中进行运行。
内存管理
Executor堆内内存内存主要有两类:Storage、Execution 两种内存空间可以动态调整,相互借用。 Executor堆外内存管理相对堆内比较简单。
Shuffle 数据传输
SparkSQL
Catalyst优化器
过程:解析逻辑计划 -> 分析逻辑计划 -> 优化逻辑计划 -> 生成物理计划 第二阶段到第三阶段
-
RBO:基于规则的优化,根据规则对节点进行转换
- RuleExecutor:每个Batch代表一个规则,Batch可以执行一次或者进行多次迭代。
- once:只执行一次
- FixedPoint:重复执行,直到Plan不再改变,或者执行到达固定次数(默认100次)
- RBO常用规则:常量累加、谓词下推、列裁剪、动态过滤裁剪、简化条件
- TransformDown:先序遍历树进行规则匹配
- TransformUP:后序遍历树进行规则匹配
- 对于join的优化不能满足
-
CBO:基于代价的优化,根据代价路径不同来选择最优方案。
- 采集表的statistics:ANALYZE TABLE ... COMPUTE STATISTICS ... FOR CLOLUMNS c1,c2
- TableStat(原始表) --(FilterEstimation)--> FilterStat --(JoinEstimation)--> JoinStat。TableStat从ANALYZE TABLE获取后续算子的Stat,通过对应的Estimation进行估算算子代价。
- JoinReorder:重新排列join的循序
- JoinSelection(Join查询算法):
- Broadcast Hash Join(BHJ):大表和小表的Join
- Shuffle Hash Join(SHJ):小表超过10M
- Sort Merge Join(SMJ):大表之间
Adaptive Query Execution(AQE):自适应查询
优化场景:
- Coalescing Shuffle Partitions(合并Shuffle分区):partition合并
-
Switching Join Strategies(转换join策略):例如优化阶段,算子statistics估算不准确,生成计划不是最优,AQE运行过程中动态获取准确的Join的LeftChild/RightChild的实际大小,SML转换为BHL。
-
Optimizing Skew Joins(优化倾斜join):优化数据倾斜,AQE根据MapStauts信息自动检测是否有倾斜,将达额partiton拆分为多个Task进行Join。
Runtime Filter
对Join算法的优化,用filter算子进行优化,将filter置于靠近数据源的一端,减少参与数据的量。
Bloom Runtime Filter(3.3版本)
Codegen
从runtime局部优化,提高CPU利用率的优化
- Experssion
- WholeStageCodegen:
挑战与实践
Shuffle稳定性解决方案
SQL执行性能问题
压榨CPU资源 CPU流水线/分支预测/乱序执行/SIMD/CPU缓存友好 向量化/Codegen