Spark原理-字节跳动大数据青训营

Spark介绍

Spark简介

Apache Spark是一个快速的、多用途的集群计算系统,相对于Hadoop MapReduce将中间结果保存在磁盘中,Spark使用了内存保存中间结果,能在数据尚未写入磁盘时在内存中进行运算。
Spark只是一个计算框架,不像Hadoop一样包含了分布式文件系统和完备的调度系统,如果要使用Spark,需要搭载其它的文件系统和成熟的调度系统。

spark执行流程


spark 应用程序以进程集合为单位在分布式集群上运行,这些进程之间通过驱动程序(Driver Program)中的SparkContext对象进行协调,通过Driver Program程序的 main 方法创建 SparkContext 的对象与集群进行交互。具体运行流程如下:

  • SparkContext 向 Cluster Manager 申请 CPU,内存等计算资源。

  • Cluster Manager 分配应用程序执行所需要的资源,在 工作节点(Worker Node)创建执行器(executor)。

  • SparkContext 将程序代码和 task 任务发送到执行器(Executor)上进行执行,代码可以是编译成的 jar 包或者 python 文件等。接着 sparkContext 会收集结果到 Driver 端。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZbDD0v69-1659409313443)(D:\学习笔记\字节跳动大数据青训营笔记\picture\spark简介\e20e2c666b868935cf7d55e577d6b552.webp)]
    注意事项如下:

  • 每个Application获取专属的executor进程,该进程在Application期间一直驻留,并以多线程方式运行tasks。这种Application隔离机制有其优势的,无论是从调度角度看(每个Driver调度它自己的任务),还是从运行角度看(来自不同Application的Task运行在不同的JVM中)。当然,这也意味着Spark Application不能跨应用程序共享数据,除非将数据写入到外部存储系统。

  • Spark与资源管理器无关,只要能够获取executor进程,并能保持相互通信就可以了。

  • 提交SparkContext的Client应该靠近Worker节点(运行Executor的节点),最好是在同一个Rack里,因为Spark Application运行过程中SparkContext和Executor之间有大量的信息交换;如果想在远程集群中运行,最好使用RPC将SparkContext提交给集群,不要远离Worker运行SparkContext。

  • Task采用了数据本地性和推测执行的优化机制。

SparkCore

RDD执行流程

RDD简介

RDD(Resilient Distributed Dataset)弹性分布式数据集,是Spark中最基本的数据(逻辑)抽象,它代表一个不可变、可分区、里面的元素可并行计算的集合。 RDD具有数据流模型的特点:自动容错、位置感知性调度和可伸缩性。RDD允许用户在执行多个查询时显式地将工作集缓存在内存中,后续的查询能够重用工作集,这极大地提升了查询速度。

Spark RDD 迭代过程

  • sparkContext 创建 RDD 对象,计算 RDD 间的依赖关系,并组成一个 DAG 有向无环图。
  • DAGScheduler 将 DAG 划分为多个 stage,并将 stage 对应的 TaskSet 提交到集群的管理中心,stage 的划分依据是 RDD 中的宽窄依赖,spark 遇见宽依赖就会划分为一个 stage,每个 stage 中包含来一个或多个 task 任务,避免多个 stage 之间消息传递产生的系统开销。
  • taskScheduler 通过集群管理中心为每一个 task 申请资源并将 task 提交到 worker 的节点上进行执行。
  • worker 上的 executor 执行具体的任务。

在这里插入图片描述

窄依赖和宽依赖

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

内存管理

在这里插入图片描述

Spark 作为一个基于内存的分布式计算引擎,Spark采用统一内存管理机制。重点在于动态占用机制。

  • 1.设定基本的存储内存和执行内存区域(spark.storage.storageFraction参数),该设定确定了双方各自拥有的空间的范围

  • 2.双方的空间都不足时,则存储到硬盘;若己方空间不足而对方空余时,可借用对方的空间

  • 3.执行内存的空间被对方占用,可以让对方占用部分转存到硬盘,然后归还借用空间

  • 4.存储内存的空间被对方占用后,无法让对方归还,需要考虑shuffle过程的很多因素实现起来较为复杂

​ 堆内(On-Heap)内存/堆外(Off-Heap)内存:Executor 内运行的并发任务共享 JVM 堆内内存。为了进一步优化内存的使用以及提高 Shuffle 时排序的效率,Spark 可以直接操作系统堆外内存,存储经过序列化的二进制数据。减少不必要的内存开销,以及频繁的 GC 扫描和回收,提升了处理性能。

​ 凭借统一内存管理机制,Spark在一定程度上提高了堆内和堆外内存资源的利用率,降低了开发者维护Spark内存的难度,但并不意味着开发者可以高枕无忧,所以如果存储内存的空间太大或者说缓存的数据过多,反而会导致频繁的全量垃圾回收。

SparkSQL原理解析

Spark为结构化数据处理引入了一个称为Spark SQL的编程模块。它提供了一个称为DataFrame的编程抽象,并且可以充当分布式SQL查询引擎。

SparkSQL执行过程

在这里插入图片描述

总体流程:

  • parser:基于antlr框架对 sql解析,生成抽象语法树

  • 变量替换:通过正则表达式找出符合规则的字符串,替换成系统缓存环境的变量

  • parser:将antlr的tree转成spark catalyst的LogicPlan也就是unresolve logical plan

  • analyzer:通过分析器,结合catalog,把logical plan和实际的数据绑定起来,将unresolve logical plan生成 logical plan;详细参考QureyExecution

  • 缓存替换:通过CacheManager,替换有相同结果的logical plan

  • logical plan优化:基于规则的优化;优化规则参考Optimizer,优化执行器RuleExecutor

  • 生成spark plan,也就是物理计划

  • spark plan准备阶段

  • 构造RDD执行,涉及spark的wholeStageCodegenExec机制,基于janino框架生成java代码并编译

Catalyst优化

Catalyst优化器组件

  • Parser模块:将SparkSql字符串解析为一个抽象语法树/AST。

  • Analyzer模块:该模块会遍历整个AST,并对AST上的每个节点进行数据类型的绑定以及函数绑定,然后根据元数据信息Catalog对数据表中的字段进行解析。

  • Optimizer模块:该模块是Catalyst的核心,主要分为RBO和CBO两种优化策略,其中RBO是基于规则优化,CBO是基于代价优化。

  • SparkPlanner模块:优化后的逻辑执行计划OptimizedLogicalPlan依然是逻辑的,并不能被Spark系统理解,此时需要将OptimizedLogicalPlan转换成physical plan(物理计划) 。

  • CostModel模块:主要根据过去的性能统计数据,选择最佳的物理执行计划。这个过程的优化就是CBO(基于代价优化)。

Optimizer模块

1.基于规则优化/Rule Based Optimizer/RBO:基于规则优化,对语法树进行一次遍历,模式匹配能够满足特定规则的节点,再进行相应的等价转换,即将一棵树等价地转换为另一棵树。SQL中经典的常见优化规则有,

  • 谓词下推(predicate pushdown)
  • 常量累加(constant folding)
  • 列值裁剪(column pruning)
  • Limits合并(combine limits)

2.基于代价优化/Cost Based Optimizer/CBO:针对每个join评估当前两张表使用每种join策略的代价,根据代价估算确定一种代价最小的方案;不同physical plans输入到代价模型(目前是统计),调整join顺序,减少中间shuffle数据集大小,达到最优输出。

AQE

AQE 可以理解成是Spark Catalyst 之上的一层,它可以在运行时修改 Spark plan(执行计划)。,它最大的亮点是可以根据已经完成的计划结点真实且精确的执行统计结果来不停的反馈并重新优化剩下的执行计划。

AQE框架三种优化场景:

  • 动态合并shuffle分区(Dynamically coalescing shuffle partitions)

  • 动态调整Join策略(Dynamically switching join strategies)

  • 动态优化数据倾斜Join(Dynamically optimizing skew joins)

RuntimeFilter

Runtime Filter是在运行时对数据进行过滤,过滤通常发生在Join阶段。当多表进行Join时,往往伴随着谓词下推等优化手段进行数据过滤,以减少Join的表的数据的扫描以及shuffle等阶段会产生的IO,从而提升查询性能。 Runtime Filter的优化方式同样也是会对数据进行过滤,但是无需用户进行设置,sql引擎会在查询时自动进行过滤优化。

Runtime优化分两类:

  • 全局优化:从提升全局资源利用率、消除数据倾斜、降低IO等角度做优化。包括AQE。

  • 局部优化:提高某个task的执行效率,主要从提高CPU与内存利用率的角度进行优化。依赖Codegen技术。

Codegen

Spark Codegen是在CBO&RBO后,将算子的底层逻辑用代码来实现(从而提高CPU占用率)的一种优化。
具体包括Expression级别和WholeStage级别的Codegen。

1.Expression级别

表达式常规递归求值语法树。需要做很多类型匹配、虚函数调用、对象创建等额外逻辑,这些overhead远超对表达式求值本身,为了消除这些overhead,Spark Codegen直接拼成求值表达式的java代码并进行即时编译。

2.WholeStage级别

传统的火山模型:SQL经过解析会生成一颗查询树,查询树的每个节点为Operator,火山模型把operator看成迭代器,每个迭代器提供一个next()接口。通过自顶向下的调用 next 接口,数据则自底向上的被拉取处理,火山模型的这种处理方式也称为拉取执行模型,每个Operator 只要关心自己的处理逻辑即可,耦合性低。

火山模型问题:数据以行为单位进行处理,不利于CPU cache 发挥作用;每处理一行需要调用多次next() 函数,而next()为虚函数调用。会有大量类型转换和虚函数调用。虚函数调用会导致CPU分支预测失败,从而导致严重的性能回退

Spark WholestageCodegen:为了消除这些overhead,会为物理计划生成类型确定的java代码。并进行即时编译和执行。

Codegen打破了Stage内部算子间的界限,拼出来跟原来的逻辑保持一致的裸的代码(通常是一个大循环)然后把拼成的代码编译成可执行文件。

业界挑战与实践

三种业内Spark面临的问题及解决策略总结

  • shuffle的稳定性问题——数据远端存储

  • SQL执行性能问题——向量法/Codegen/两者结合,例如Intel:OAP

  • 参数推荐/作业诊断——自动化
    辑保持一致的裸的代码(通常是一个大循环)然后把拼成的代码编译成可执行文件。

业界挑战与实践

三种业内Spark面临的问题及解决策略总结

  • shuffle的稳定性问题——数据远端存储

  • SQL执行性能问题——向量法/Codegen/两者结合,例如Intel:OAP

  • 参数推荐/作业诊断——自动化

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值