青训营Spark 原理与实践
2022の夏天,半壶水响叮当的我决定充实一下自我
一、内容介绍
青训营
大数据处理引擎Spark介绍
Spark生态组件:
- Spark Core:Spark核心组件,它实现了Spark的基本功能,包含任务调度、内存管理、错误恢复、与存储系统交互等模块。
- Spark SQL:用来操作结构化数据的核心组件,通过Spark SQL可以直接查询Hive、HBase等多种外部数据源中的数据。
- Spark Structured Streaming:Spark提供的流式计算框架,支持高吞吐量、可容错处理的实时流式数据处理。
- MLlib:Spark提供的关于机器学习功能的算法程序库,包括分类、回归、聚类、协同过滤算法等,还提供了模型评估、数据导入等额外的功能。
- GraphX:Spark提供的分布式图处理框架,拥有对图计算和图挖掘算法的API接口以及丰富的功能和运算符。
- 独立调度器、Yarn、Mesos、Kubernetes:Spark框架可以高效地在一个到数千个节点之间伸缩计算,集群管理器则主要负责各个节点的资源管理工作,为了实现这样的要求,同时获得最大灵活性,Spark支持在各种集群管理器(Cluster Manager)上运行。
Spark 运行架构和工作原理:
- Application(应用):Spark上运行的应用。Application中包含了一个驱动器(Driver)进程和集群上的多个执行器(Executor)进程。
- Driver Program(驱动器):运行main()方法并创建SparkContext的进程。
- Cluster Manager(集群管理器):用于在集群上申请资源的外部服务(如:独立部署的集群管理器、Mesos或者Yarn)
- Worker Node(工作节点):集群上运行应用程序代码的任意一个节点。
- Executor(执行器):在集群工作节点上为某个应用启动的工作进程,该进程负责运行计算任务,并为应用程序存储数据。
- Task(任务):执行器的工作单元。
- Job(作业):一个并行计算作业,由一组任务(Task)组成,并由Spark的行动(Action)算子(如:save、collect)触发启动。
- Stage(阶段):每个Job可以划分为更小的Task集合,每组任务被称为Stage。
Spark目前支持几个集群管理器:
- Standalone :Spark 附带的简单集群管理器,可以轻松设置集群。
- Apache Mesos:通用集群管理器,也可以运行 Hadoop MapReduce 和服务应用程序。(已弃用)
- Hadoop YARN: Hadoop 2 和 3 中的资源管理器。
- Kubernetes:用于自动部署、扩展和管理容器化应用程序的开源系统。
SparkCore
- RDD(Resilient Distributed Dataset):弹性分布式数据集,是一个容错的、并行的数据结构
- RDD算子:对任何函数进行某一项操作都可以认为是一个算子,RDD算子是RDD的成员函数
- Transform(转换)算子: 根据已有RDD创建新的RDD
- Action(动作)算子: 将在数据集上运行计算后的数值返回到驱动程序,从而触发真正的计算
- DAG(Directed Acyclic Graph): 有向无环图,Spark中的RDD通过一系列的转换算子操作和行动算子操作形成了一个DAG
- DAGScheduler:将作业的DAG划分成不同的Stage,每个Stage都是TaskSet任务集合,并以TaskSet为单位提交给TaskScheduler。
- TaskScheduler:通过TaskSetManager管理Task,并通过集群中的资源管理器(Standalone模式下是Master,Yarn模式下是ResourceManager)把Task发给集群中Worker的Executor
- Shuffle:Spark中数据重分发的一种机制。
SparkSQL
- DataFrame: 是一种以RDD为基础的分布式数据集, 被称为SchemaRDD
- Catalyst:SparkSQL核心模块,主要是对执行过程中的执行计划进行处理和优化
- DataSource:Spark/SQL支持通过 DataFrame 接口对各种数据源进行操作。
- Adaptive Query Execution:自适应查询执行
- Runtime Filter:运行时过滤
- Codegen:生成程序代码的技术或系统,可以在运行时环境中独立于生成器系统使用
SparkSql执行过程:
- Unresolved Logical Plan:未解析的逻辑计划,仅仅是数据结构,不包含任何数据信息。
- Logical Plan:解析后的逻辑计划,节点中绑定了各种优化信息。
- Optimized Logical Plan:优化后的逻辑计划
- Physical Plans:物理计划列表
- Selected Physical Plan 从列表中按照一定的策略选取最优的物理计划
业界挑战与实践
- 向量化(vectorization):将循环转换为向量操作的编译器优化
- 代码生成(Codegen:Code generation):生成程序代码的技术或系统,可以在运行时环境中独立于生成器系统使用
二、大数据处理引擎 Spark介绍
Spark:大数据批处理计算环节
大数据处理技术栈
常见大数据处理链路
开源大数据处理引擎
Batch批式 Streaming流式 OLAP联机分析处理
2.1Spark概念
官网:用于大规模数据处理的统一分析(计算)引擎
多语言引擎,可用于单机结点或集群
可执行数据工程,数据科学,机械学习
特点:
1.Batch/streaming data
用统一方式处理流批数据,多语言选择:Python,SQL,Scala,Java, R
2.SQL分析
用仪表板和临时报告等快速执行分布式ANSI(快速) SQL查询。运行速度比大多数数据仓库都快
3.大规模数据科学
对千兆(PB)比例尺数据执行探索性数据分析(EDA,电子设计自动化)
4.机器学习
单机上的训练机学习算法,并方便扩大到大规模计算机集群上。
2.2Spark生态&特点
见上 Spark生态组件
- 统一引擎,支持多种分布式场景
- 多语言支持
- 可读写丰富数据源
- 丰富灵活的API/算子
- 支持K8S/YARN/Mesos资源调度
2.3Spark多语言支持
SELECT
name.first AS first_name,
name.last AS last_name,
age
FROM json.`logs.json`
WHERE age > 21;
Dataset df = spark.read().json ( "logs.json") ;
df.where("age > 21")
.select("name.first" ).show( ) ;
val df = spark.read.json ("logs.json")
df.where("age > 21")
.select ( "name.first" ).show()
df <- read.json(path = "logs.json")
df <- filter(df,df$age > 21)
head(select(df, df$name.first))
df = spark.read.json("logs.json")
df.where("age > 21").select("name.first").show()
2.4Spark丰富数据源
-
内置 DataSource: Text
Parquet/ORC
JSON/CSV
JDBC -
自定义DataSource 实现 DataSource V1/V2 API
HBase/Mongo/ElasticSearch/...
一个用于Apache SPark的第三方包的社区索引
spark-packages.org
2.5Spark丰富API/算子(函数操作)
- SparkCore ->RDD(弹性分布式数据集,是一个容错的、并行的数据结构)
map/filter/flatMap/mapPartitions
地图/过滤器/平面地图/地图部分
repartition/groupBy/reduceByljoin/aggregate
重新分区/组/还原-Byljoin/聚合
foreach/foreachPartition
预测/预测划分
count/max/min
计数/最大/分钟
- SparkSQL-> DataFrame(RDD为基础分布式数据集,传统数据库二维表格,含数据,数据结构信息,Hive)
select/filterlgroupBylagg/join/union/orderByl...
Hive(单元) UDF/自定义UDF
2.6Spark运行架构&部署方式
支持多资源调度:
运行/执行架构:master&slave架构
Cluster Manager集群管理器:控制集群,监控worker结点,负责资源管理与调度
worker结点,worker manager:控制计算节点,开启Executor进程,负责Executor与Task执行
Tast任务:Driver Program,一个app mast,整个应用管理者
Spark Application只有一个Driver Program
Driver Program:负责作业任务调度,一个JVM进程,运行程序内函数,创建一个Spark Context上下文(控制整个应用生命周期)
Executor:实际执行任务Tast
Spark Context链接Cluster Manager
Cluster Manager依据参数CPU,内存分配资源,启动Executor进程
Driver Program将程序Application划分Stage(阶段)
Stage(阶段)由相同task任务构成,task分别作用于不同分区待处理数据
Driver 向Executor发送task,Executor下载task下载运行依赖,环境,并执行task,并将task运行状态反馈Driver,Driver进行状态更新,调用task给Executor执行,直到次数或时间限制
部署方式:
- Spark Local Mode
本地测试/单进程多线程模式
spark-sql --master local[*] ...
- Spark Standalone Mode
需要启动Spark的Standalone集群的Master/Worker
spark-sql --master spark://${master_ip]:${port}...
- On YARN/K8S
依赖外部资源调度器(YARN/K8S)
spark-sql --master yarn ...
spark-sql --master k8s://https:/l<k8s-apiserver-host>:<k8s-apiserver-port>
2.7Spark下载
2.8Spark提交命令
环境变量
spark-shell
spark-sql
pyspark
2.8Spark提交一个简单任务
package org.apache.spark.examples
import scala.math.random
import org.apache.spark.sql.sparkSession
/** Computes an approximation to pi
计算π的近似值
*/
object SparkPi {
def main(args: Array [string]): Unit = {
val spark = SparkSession
.builder
.appName("spark Pi")
.get0rCreate()
val slices = if (args.length > 0) args(0).toInt else 2
val n = math.min(100000L * slices,Int.MaxValue).toInt
// avoid overflow
val count = spark.sparkContext.parallelize( 1 until n,slices).map { i =>
val x = random * 2 - 1
val y = random * 2 - 1
if (x*x + y*y <= 1) 1 else 0
}.reduce(_ + _)
println(s"Pi is roughly ${4.0 * count / (n - 1)}")
spark.stop()
}
}//蒙特卡罗随机数求pi
编译成jar包之后,使用spark-submit提交
2.8Spark UI查看任务更详细信息
2.9Spark性能benchmark
三、SparkCore原理解析
- RDD(Resilient Distributed Dataset):弹性分布式数据集,是一个容错的、并行的数据结构
- DAG/task调度
- Shuffle:Spark中数据重分发的一种机制。
- 内存管理机制
3.1基本单元:RDD
- RDD(Resilient Distributed Dataset):弹性分布式数据集,是一个容错的、并行的数据结构
描述RDD的五要素
| 要素 | 解释 |
|---|---|
| Partitions/分区 | 分区个数(集合默认CPU数)对应task处理,并行计算数量 |
| Compute/计算 | 计算函数个数与分区数对应 |
| Dependencies/相依性 | RDD依赖其他RDD(前后依赖关系,不用从头开始) |
| Partitioner/分割器 | 键值RDD的分区器(例如,抛出RDD是hash的) |
| PreferredLocations/预测位置 | 可计算每个分区的优先位置(例如,HDFS 文件的块位置) |
abstract class RDD{
def getPartitions: Array[Partition]...
def compute(split: Partition...
def getDependencies: Seq[Dependency[_]]...
val partitioner.Option[Partitioner]...
def getPreferredLocations(split:Partition)...
//算子
def map(f:T=> U): RDD[U]
def filter(f:T=> Boolean): RDD[T]
...
def count(): Long
def cache()//缓存
def persist()//缓存
}
3.1创建RDD
3.2两类RDD
- Transform算子:生成一个RDD
- Action算子:(commit)触发Job提交
3.3RDD依赖
RDD依赖下可依据子分区数据恢复运算
3.4RDD执行
- Job: RDD action算子触发
- Stage:依据宽依赖划分
- Task: Stage内执行单个partition任务
划分Stage的整体思路:从后往前推,遇到宽依赖就断开,划分为一个Stage。遇到窄依赖,就将这个RDD加入该Stage中,DAG最后一个阶段会为每个结果的Partition生成一个ResultTask。每个Stage里面的Task数量由最后一个RDD的Partition数量决定,其余的阶段会生成ShuffleMapTask。
当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提供。
3.5 Scheduler/调度程序
3.6 Memory Management/内存管理机制
Spark 作为一个基于内存的分布式计算引擎,Spark采用统一内存管理机制。重点在于动态占用机制。
-
设定基本的存储内存(Storage)和执行内存(Execution)区域,该设定确定了双方各自拥有的空间的范围,UnifiedMemoryManager统一管理Storage/Execution内存
-
双方的空间都不足时,则存储到硬盘;若己方空间不足而对方空余时,可借用对方的空间
-
当Storage空闲,Execution可以借用Storage的内存使用,可以减少spill等操作, Execution内存不能被Storage驱逐。Execution内存的空间被Storage内存占用后,可让对方将占用的部分转存到硬盘,然后"归还"借用的空间
-
当Execution空闲,Storage可以借用Execution内存使用,当Execution需要内存时,可以驱逐被Storage借用的内存,可让对方将占用的部分转存到硬盘,然后"归还"借用的空间
3.6.1 多任务间内存分配
UnifiedMemoryManager 统一管理多个并发Task的内存分配,空间不够则阻塞,直到足够空间唤醒
每个Task获取的内存区间为1/(2*N)~1/N,N为当前Executor中正在并发运行的task数量
3.7 Shuffle
Map与Reduce间数据处理,重分发过程
spark.shuffle.manager -> sort
trait ShuffleManager {
def registerShuffle...def getWriter...
def getReader...
def unregisterShuffle...
}
class SortShuffleManager extends ShuffleManager
四、SparkSQL原理解析
4.1.基本流程
SQL Query查询语句转抽象ST语法树->Unresolved Logical Plan未解析的逻辑计划->Analysis解析计划,绑定数据,依据数据Catalog信息对数据解析->解析后逻辑计划->逻辑计划优化Catalyst(RBO/CBO)-> Physical Planning将逻辑转物理实行计划->依据Cost Model过去信息统计(CBO)->选择最佳物理执行计划->代码转换为JAVA,实现RDDs
4.2.Catalyst优化器
- RBO
- CBO
4.3.自查询Adaptive Query Execution(AQE)
边执行,边优化,依完成结点,真实统计不停反馈
4.3.1 AQE- Coalescing Shuffle Partitions
4.3.2 AQE- Switching Join Strategies
4.3.3 AQE- Optimizing Skew Joins
4.4 Runtime Filter
4.5 Bloom Runtime Filter
链接 [spark/q16.sql at a78d6ce376edf2a8836e01f47b9dff5371058d4c · apache/spark · GitHub]
4.6 Codegen - Expression
4.6.1 Codegen - WholeStageCodegen
动态生成代码,Janino即时编译执行
五、业界挑战与实践
5.1 Shuffle稳定性
问题:在大规模作业下,开源ExternalShuffleService(ESS)的实现机制容易带来大量随机读导致的磁盘IOPS瓶颈、Fetch请求积压等问题,进而导致运算过程中经常会出现 Stage重算甚至作业失败,继而引起资源使用的恶性循环,严重影响SLA
解决:
5.2 SQL执行性能
问题:
解决:
5.3 参数推荐/作业诊断
晚安玛卡巴卡
快乐暑假