这是我参与「第四届青训营 」笔记创作活动的第1天。
1.引入
1.1 大数据处理栈
大数据处理栈分为4个部分:
- 数据
- 存储
- 计算
- 应用
Spark在处理栈中处于计算部分,下面就开始对Spark进行介绍
2 Spark介绍
Spark是基于内存计算的大数据并行计算框架,比MapReduce计算框架具有更高的实时性,同时具有高效容错性和可伸缩性,在学习Spark操作之前,首先介绍Spark运行架构。
2.1 Spark生态和特点
核心:
SparkCore Spark的核心组件,可任务调度、内存管理、错误恢复、存储交互
SparkSQL 操作结构数据的组件,可查询Hive、HBase
Spark Sturctured Streaming 流逝型框架,可支持高吞吐、实时的数据
MLlib Spark内部自带机器学习算法数据库
2.2 Spark运行架构和部署方式
Spark应用在集群上运行时,包括了多个独立的进程,这些进程之间通过驱动程序(Driver Program)中的SparkContext对象进行协调,SparkContext对象能够与多种集群资源管理器(Cluster Manager)通信,一旦与集群资源管理器连接,Spark会为该应用在各个集群节点上申请执行器(Executor), 用 于 执 行 计 算 任 务 和 存 储 数 据。Spark将应用程序代码发送给所申请到的执行器,SparkContext对象将分割出的任务(Task)发送给各个执行器去运行。
Spark对底层的集群管理器一无所知,只要Spark能够申请到执行器进程,能与之通信即可。这种实现方式可以使Spark比较容易的在多种集群管理器上运行,例如Mesos、Yarn。
驱动器程序在整个生命周期内必须监听并接受其对应的各个执行器的连接请求,因此驱动器程序必须能够被所有Worker节点访问到。
因为集群上的任务是由驱动器来调度的,所以驱动器应该和Worker节点距离近一些,最好在同一个本地局域网中,如果需要远程对集群发起请求,最好还是在驱动器节点上启动RPC服务响应这些远程请求,同时把驱动器本身放在离集群Worker节点比较近的机器上。
2.3 Spark原理解析
SparkCore:
spark的核心与基础,实现了Spark的基本功能,包含任务调度,内存管理,错误恢复与存储系统交互等模块
RDD概念
简介:
RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象,代表一个不可变、可分区、里面的元素可并行计算的集合。
Spark所有的运算以及操作都建立在 RDD 数据结构的基础之上。
RDD包含5个特征:
- 一个分区的列表
- 对于每一个分区都有一个计算函数
- 存在对其他RDDs的依赖(宽依赖、窄依赖)的列表
- 对于key-value的RDD 有一个分区器
- 有一个位置优先的函数
RDD特点
- 分区
- 只读
- 依赖
- 持久化(缓存)
内存管理
Executor内存主要有两类:Storage、Execution
UnifiedMemoryManager统一管理Storage/Execution内存
Storage和Execution内存使用是动态调整,可以相互借用
当Storage空闲,Execution可以借用Storage的内存使用,可以减少spill 等操作,Execution使用的内存不能被Storage 驱逐 当Execution空闲,Storage可以借用Execution的内存使用,
当Execution需要内存时,可以驱逐被Storage借用的内存,直到spark.memory.storageFraction边界
SparkSQL
概述:
Spark SQL是Spark的其中一个模块,用于结构化数据处理。与基本的Spark RDD API不同,Spark SQL提供的接口为Spark提供了有关数据结构和正在执行的计算的更多信息,Spark SQL会使用这些额外的信息来执行额外的优化。
Catalyst
-
Rule Based Optimizer(RBO): 基于规则优化,对语法树进行一次遍历,模式匹配能够满足特定规则的节点,再进行相应的等价转换。
-
Cost Based Optimizer(CBO): 基于代价优化,根据优化规则对关系表达式进行转换,生成多个执行计划,然后CBO会通过根据统计信息(Statistics)和代价模型(Cost Model)计算各种可能执行计划的代价,从中选用COST最低的执行方案,作为实际运行方案。CBO依赖数据库对象的统计信息,统计信息的准确与否会影响CBO做出最优的选择。
AQE
AQE对于整体的Spark SQL的执行过程做了相应的调整和优化,它最大的亮点是可以根据已经完成的计划结点真实且精确的执行统计结果来不停的反馈并重新优化剩下的执行计划。
AQE框架三种优化场景:
-
动态合并shuffle分区(Dynamically coalescing shuffle partitions)
-
动态调整Join策略(Dynamically switching join strategies)
-
动态优化数据倾斜Join(Dynamically optimizing skew joins)
RuntimeFilter
实现在Catalyst中。动态获取Filter内容做相关优化,当我们将一张大表和一张小表等值连接时,我们可以从小表侧收集一些统计信息,并在执行join前将其用于大表的扫描,进行分区修剪或数据过滤。可以大大提高性能
Runtime优化分两类:
-
全局优化:从提升全局资源利用率、消除数据倾斜、降低IO等角度做优化。包括AQE。
-
局部优化:提高某个task的执行效率,主要从提高CPU与内存利用率的角度进行优化。依赖Codegen技术。
Codegen
从提高cpu的利用率的角度来进行runtime优化。
- Expression级别
表达式常规递归求值语法树。需要做很多类型匹配、虚函数调用、对象创建等额外逻辑,这些overhead远超对表达式求值本身,为了消除这些overhead,Spark Codegen直接拼成求值表达式的java代码并进行即时编译
- WholeStage级别
传统的火山模型:SQL经过解析会生成一颗查询树,查询树的每个节点为Operator,火山模型把operator看成迭代器,每个迭代器提供一个next()接口。通过自顶向下的调用 next 接口,数据则自底向上的被拉取处理,火山模型的这种处理方式也称为拉取执行模型,每个Operator 只要关心自己的处理逻辑即可,耦合性低。
火山模型问题:数据以行为单位进行处理,不利于CPU cache 发挥作用;每处理一行需要调用多次next() 函数,而next()为虚函数调用。会有大量类型转换和虚函数调用。虚函数调用会导致CPU分支预测失败,从而导致严重的性能回退。