Spark原理与实践 | 青训营笔记

118 阅读6分钟

这是我参与「第四届青训营」笔记创作活动的的第5天

课程目录

01.大数据处理引擎Spark介绍

02.SparkCore 原理解析

03.SparkSQL原理解析

04.业界挑战与实践

01.大数据处理引擎Spark介绍

1.1大数据处理技术栈

截屏2022-07-30 15.08.51.png

1.2常见大数据处理链路

截屏2022-07-30 15.10.00.png

1.3开源大数据处理引擎

截屏2022-07-30 15.11.54.png

1.4 什么是Spark?

官网: spark.apache.org/

github: https://github,com/apache/spark

Unified engine for large scale data analytics

Apache SparkTM is a multi-language engine for executing data engineering, data science, and machine learning on single- node machines or clusters.

截屏2022-07-30 15.13.59.png

1.4 Spark版本演进

截屏2022-07-30 15.15.03.png

1.4 Spark生态 & 特点

截屏2022-07-30 15.15.49.png

▪️ 统一引擎,支持多种分布式场景

▪️ 多语言支持

▪️ 可读写丰富数据源

▪️ 丰富灵活的AP/算子

▪️ 支持K8S/YARN/Mesos资源调度

1.5 Spark特点 - 多语言支持

SQL

截屏2022-07-30 15.18.09.png

Java/Scala

截屏2022-07-30 15.19.23.png

R

截屏2022-07-30 15.19.47.png

Python

截屏2022-07-30 15.20.32.png

1.5 Spark特点 - 丰富数据源

DataSource

1)Text

2)Parquet/ORC

3)JSON/CSV

4)JDBC

截屏2022-07-30 15.23.01.png

自定义 DataSource 实现 DаtаЅоurсеV1/V2 АРІ

HBase/Mongo/ElasticSearch/...

A community index of third-party packages for Apache Spark.

https://spark-package

1.5 Spark特点 - 丰富的API/算子

SparkCore -> RDD

map/filter/flatMap/mapPartitions

repartition/groupBy/reduceBy/join/aggregate

foreach/foreachPartition

count/max/min

...

SparkSQL -> DataFrame

select/filter/groupBy/agg/join/union/orderBy

Hive UDF/自定义 XUDF

1.6 Spark运行架构 & 部署方式

截屏2022-07-30 15.27.31.png

Spark Local Mode

本地测试/单进程多线程模式

spark-sql --master local

Spark Standalone Mode

需要启动Spark的Standalone集群的Master/Worker

spark-sql --master spark://masterip:{master_ ip}:{port}

On YARN/K8S

依赖外部资源调度器(YARN/K8S)

spark-sql --master yarn...

spark- sql --master k8s://https://<k8s· apiserver-host>:

1.7 Spark下载编译

Spark包

编译:

git clone -b master github.com/apache/spar…

cd spark

./dev/make-distribution.sh --name custom-spark --pip --r --tgz -Psparkr -Phive -Phive- thriftserver -Pmesos -Pyarn !

编译参数可选,详见官网building-sparkttps://spark.apache.org/docs/latest/building spark.html)

编译完后会在目录下生成一个tgz包

下载:

官网download -->spark.apache.org/downloads.h…

1.8 Spark包概览

截屏2022-07-30 15.35.37.png

截屏2022-07-30 15.36.50.png

1.9 Spark提交命令

环境变量

截屏2022-07-30 15.37.53.png

spark-shell

截屏2022-07-30 15.38.45.png

spark-sql

截屏2022-07-30 15.39.18.png

pyspark

截屏2022-07-30 15.40.02.png

1.10 提交一个简单任务

SparkPi scala 代码

截屏2022-07-30 15.41.19.png

编译成jar包之后,使用spark-submit提交

截屏2022-07-30 15.42.24.png

1.11 Spark UI

截屏2022-07-30 15.43.20.png

截屏2022-07-30 15.43.49.png

1.12 Spark性能 benchmark

TPC-DS/TPC-H benchmark

github.com/databricks/… sql-perf

截屏2022-07-30 15.45.13.png

02.SpatkCore原理解析

2.1 SparkCore

截屏2022-07-30 15.46.28.png

2.2什么是RDD?

RDD(Resilient Distributed Dataset)

Represents an immutable, partitioned collection of elements that can be operated on in parallel.

abstract class RDD {

def getPartitions: Array[Partition]

def compute(split: Partition..

def getDependencies: Seq[Dependency[ ]]

val partitioner. Option[Partitioner]...

def getPreferredL ocations(split: Partition)

//算子

def map(f: T => U): RDD[U]

def filter(f: T => Boolean): RDD[T]

...

def count(): Long

def cache() //缓存

def persist() //缓存

描述RDD的五要素

截屏2022-07-30 15.55.13.png

2.2.1如何创建RDD?

内置RDD

ShuffleRDD/HadoopRDD/JDBCRDD

/KafkaRDD/ UnionRDD/MapPartitionsRDD/...

截屏2022-07-30 15.57.18.png

自定义RDD

class CustomRDD(...) extends RDD {}

实现五要素对应的函数

2.2.2 RDD算子

➢两类RDD算子

Transform算子:生成一个新的RDD

map/filter/flatMap/groupByKey/reduceByKeyl...

截屏2022-07-30 16.00.10.png

Action算子:触发Job提交

collect/count/take/saveAs T extFile/...

截屏2022-07-30 16.01.17.png

image.png

2.2.3RDD依赖

截屏2022-07-30 16.02.53.png

RDD依赖:描述父子RDD之间的依赖关系(lineage)。

➢窄依赖:父RDD的每个partition至多对应一个子RDD分区。

1) NarrowDependency

def getParents(partitionld: Int): Seq[lnt]

2) OneToOneDependency

override def getParents(partitionld: Int): List[lnt]

3) RangeDependency override def getParents(partitionld: Int): List[lnt] =

if (partitionld >= outStart && partitionld < outStart + length)

List(partitionld - outStart + inStart)

4) PruneDependency

➢宽依赖:父RDD的每个partition都可能对应多个子RDD分区。
ShuffieDependency

截屏2022-07-30 16.08.20.png

2.2.4 RDD执行流程

Job: RDD action算子触发

Stage:依据宽依赖划分

Task: Stage内执行单个partition任务
截屏2022-07-30 16.13.48.png

2.3 Scheduler

截屏2022-07-30 16.15.30.png

根据ShuffleDependency切分Stage,并按照依赖顺序调度Stage,为每个Stage生 成并提交TaskSet到TaskScheduler

根据调度算法(FIFO/FAIR)对多个TaskSet进行调度,对于调度到的TaskSet, 会将Task调度(locality)到相关Executor.上面执行, Executor SchedulerBackend提供

Locality: PROCESS_ LOCAL, NODE_ LOCAL, RACK_ LOCAL, ANY

2.4 Memory Management

截屏2022-07-30 16.18.38.png

➢Executor 内存主要有两类: Storage 、Execution

➢UnifiedMemoryManager 统一管理Storage/Execution内存

➢Storage 和Execution内存使用是动态调整,可以相互借用

➢当Storage空闲,Execution 可以借用Storage的内存使用,

➢可以减少spill 等操作,Execution 使用的内存不能被Storage驱逐

➢当Execution 空闲,Storage 可以借用Execution的内存使用

➢当Execution需要内存时,可以驱逐被Storage借用的内存,直到

➢spark.memory.storageFraction边界

截屏2022-07-30 16.20.20.png

UnifiedMemoryManager统一管理多 个并发Task的内存分配

每个Task获取的内存区间为1/(2*N) ~ 1/N,N为当前Executor中正在并发运行的task数量

2.5 Shuffle

spark.shuffle.manager -> sort

trait ShuffleManager f

def registerShuffle...

def getWriter....

def getReader..

def unregisterShuffle...

}

class SortShuffleManager extends ShuffleManager

截屏2022-07-30 16.23.17.png

截屏2022-07-30 16.23.59.png

每个MapTask生成一个Shuffle数据文 件和一个index文件

dataFile中的数据按照partitionld进行排序,同一个partitionld的数据聚集在一起indexFile保存了所有paritionld在dataFlle中的位置信息,方便后续ReduceTask能Fetch到对应partitionld 的数据

截屏2022-07-30 16.25.15.png

截屏2022-07-30 16.25.54.png

shuffle write 的文件被 NodeManager中的 Shuffle Service 托管 , G供后续ReduceTask 进行 shuffle fetch, 如果Executor空闲,DRA可以进行回收

03.SparkSQL原理解析

SparkSQL

截屏2022-07-30 16.29.25.png

截屏2022-07-30 16.29.53.png

3.1 Catalyst优化器

截屏2022-07-30 16.30.58.png

3.1 Catalyst优化器 - RBO

截屏2022-07-30 16.32.07.png

Batch执行策略:

Once ->只执行一次

FixedPoint ->重复执行,直到plan不再改变,或者执行达到固定次数(默认100次)

截屏2022-07-30 16.33.15.png

Rule Based Optimizer(RBO)

ConstantFolding/PushDownPredicates/ColumnPruning /DynamicFilterPruning/SimplifyConditionals/..

object PushDownPredicates extends Rule { def apply(plan: LogicalPlan): LogicalPlan - plan transformDown{ ... } }

transformDown先序遍历树进行规则匹配

transformUp后序遍历树进行规则匹配

select * from t x join t1 y on x,a=y.c where x,a>1 and y.c- 2

截屏2022-07-30 16.35.58.png

Cost Based Optimizer(CBO)

采集表的ѕtаtіѕtісѕ

ANALYZE TABLE .. COMPUTE STATISTICS ..FOR COLUMNS c1,c2

截屏2022-07-30 16.37.37.png

截屏2022-07-30 16.38.01.png

TableStatMANALYZE TABLE获取 后续的算子的Stat通过对应的E stimation进行估算

打开参数 spark.sql.cbo.enabled -> true

JoinReorder

截屏2022-07-30 16.39.34.png

JoinSelection

Broadcast Hash Join(BHJ)

Shuffle Hash Join(SHJ)

Sort Merge Join(SMJ)

3.2 Adaptive Query Execution(AQE)

截屏2022-07-30 16.40.50.png

每个Task结束会发送MapStatus信息给Driver

Task的MapStatus中包含当前Task Shuffle产生的每个Partition的size统计信息

Driver获取到执行完的Stages的MapStatus信息之后,按照MapStatus中partition大小信息识别匹配一些优化场景 ,然后对后续未执行的Plan进行优化

**目前支持的优化场景: **

  • Partiton合并,优化shuffle读取,减少reduce task个数

  • SMJ -> BHJ

  • Skew Join优化

3.2 AQE - Coalescing Shuffle P artitions

Partition s# (coalescing shufile partitions

截屏2022-07-30 16.44.22.png

问题

spark.sqlshumeon作业粒度参数, 一个作业中所有Stage都一样,但是每个Stage实际处理的数据不-样,可能某些Stage的性能比较差

比如: -partition参数对某个Stage过大,则可能单个partition的大小比较小,而且Task个数会比较多,shuffle fetch阶段产生大量的小块的随机读,影响性能

-parition参数对某个Stage过小,则可能单个partition的大小比 较大,会产生更多的spill或者OOM

作业运行过程中,根据前面运行完的Stage的MapStatus中实际的partiton大小信息,可以将多个相邻的较小的partiton进行动态合并,由一个Task读取进行处理

spark.sql.adaptive.coalescePartitions .enabled

spark.sql. adaptive.coalescePartitions .initialPartitionNum

spark.sql.adaptive.advisoryPartitionSizelnBytes

3.2 AQE Switching Join Strategies

SortMergeJoin (SMJ) -> BroadcastHashJoin (BHJ)

截屏2022-07-30 16.47.26.png

问题

Catalyst Optimizer优化阶段,算子的statistics估算不准确,生成的执行计划并不是最优

AQE运行过程中动态获取准确Join的leftChild/rightChild的实际 大小,将SMJ转换为BHJ

3.2 AQE - Optimizing Skew Joins

Skew Join

截屏2022-07-30 16.49.22.png

AQE根据MapStatus信息自动检测是否有倾斜

将大的partition拆分成多个Task进行Join

spark.sql.adaptive.skewJoin.enabled

spark.sql.adaptive.skewJoin.skewedPartitionFactor

spark.sql.adaptive.skewJoin.skewedPartitionThresholdInBytes

3.3 Runtime Filter

截屏2022-07-30 16.51.48.png

3.4 Bloom Runtime Filter

tpcds/q16.sql: ii AND cs1.cs_ call center sk Cc_ call_ center_ sk

截屏2022-07-30 16.52.48.png

3.5 Codegen - Expression

表达式(Expression)

截屏2022-07-30 16.54.01.png

将表达式中的大量虚函数调用压平到一个函数内部,类似手写代码

截屏2022-07-30 16.55.27.png

动态生成代码,Janino 即时编译执行

3.5 Codegen - WholeStageCodegen

算子/WholeStageCodegen

select (a+1)*a from t where a=1 火山模型(Volcano)

截屏2022-07-30 16.57.49.png

算子之间大量的虚函数调用,开销大

将同一个Stage中的多个算子压平到一个函数内部进行执行

动态生成代码,Janino 即时编译执行

截屏2022-07-30 16.59.14.png

截屏2022-07-30 17.00.29.png

一个SQL包含多个Stage的WholeStageCodegen

截屏2022-07-30 17.01.33.png

04.业界挑战

4.1 Shuffle稳定性问题

截屏2022-07-30 17.02.24.png

在大规模作业下,开源ExternalShuffleService(ESS)的实现机制容易带来大量随机读导致的磁盘IOPS瓶颈、Fetch请求积压等问题,进而导致运算过程中经常会出现Stage重算甚至作业失败,继而引起资源使用的恶性循环,严重影响SLA.

4.1 Shuffle稳定性解决方案

截屏2022-07-30 17.04.27.png

4.2 SQL执行性能问题

截屏2022-07-30 17.05.29.png

➢ 压榨CPU3源

CPU流水线/分支预测/乱序执行 /SIMD/CPU缓存友好/...

Vectorized / Codegen ?

C++ / Java ?

4.2 SQL执行性能解决方向

Photon: C++实现的向量化执行引擎

截屏2022-07-30 17.07.55.png

Intel: OAP/gazelle_ plugin github.com/oap-project…

截屏2022-07-30 17.08.30.png

4.3参数推荐/作业诊断

Spark参数很多,资源类/Shuffle/Join/Agg/... 调参难度大

参数不合理的作业,对资源利用率/Shuffe稳定性/性能有非常大影响

同时,线.上作业失败/运行慢,用户排查难度大

      |

自动参数推荐/作业诊断