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

89 阅读6分钟

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

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

1.1大数据处理技术栈

  • 应用:

BI报表/实时大盘/广告/推荐/...

Spark/Flink/Presto/Impala/ClickHouse/..

YARN/K8S

  • 计算:

MetaStore

Parquet/ORC/DeltaLake/Hudi/Iceberg/..

HDFS/Kafka/HBase/Kudu/TOS/S3/...

  • 存储:

Volume/Variety/Velocity

平台/管理/安全/...

  • 数据:

On-Premise/On-Cloud

1.2 常见大数据处理链路

image.png

1.3开源大数据处理引擎

Batch:hadoop map Reduce, Apache Spark, Hive

Streaming:Flink

OLAP:presto, ClickHouse, Impala, DORIS

1.4 Spark

什么是Spark?

官网:spark.apache.org/

github: github.com/apache/spar…

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.

image.png

Spark 版本演进

image.png

Spark生态&特点

image.png

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

  • 多语言支持

  • 可读写丰富数据源

  • 丰富灵活的API/算子

  • 支持K8S/YARN/Mesos资源调度

1.5 Spark特点

多语言支持

SQL, Java/Scala, Python, R

丰富数据源

  • 内置DataSource

    Text

    Parquet/ORC

    JSON/CSV

    JDBC

  • 自定义DataSource

    实现DataSourceV1/V2 API

    HBase/Mongo/ElasticSearch/..

    A community index of third-party packages

    for Apache 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/自定义UDF

1.6 Spark运行架构&部署方式

image.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://: ...

1.7 Spark 下载编译

Spark 包

  • 编译:

git clone –b master https://github.com/apache/spark.git

cd spark

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

-Pmesos -Pyarn

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

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

  • 下载:

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

image.png

1.8 Spark包概览

image.png

image.png

1.9 Spark 提交命令

环境变量

image.png

spark-shell

image.png

spark-sql

image.png

pyspark

image.png

1.10 提交一个简单任务

SparkPi scala 代码

image.png

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

image.png

image.png

1.11 Spark UI

image.png

Job运行中-> http://driverhost:{driver_host}:port

Job运行完-> SparkHistoryServer可查看

image.png

1.12 Spark性能benchmark

TPC-DS/TPC-H benchmark

github.com/databricks/…

image.png

02.SparkCore 原理解析

2.1 SparkCore

image.png

image.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 getPreferredLocations(split: Partition).…

//算子

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

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

def count(): Long

def cache()//缓存

def persist()//缓存

描述RDD的五要素 image.png

2.2.1 如何创建RDD?

  • 内置RDD

ShuffleRDD/HadoopRDD/JDBCRDD

/KafkaRDD/ UnionRDD/MapPartitionsRDD/..

image.png

  • 自定义RDD

class CustomRDD(...) extends RDD {}

实现五要素对应的函数

2.2.2 RDD算子

两类RDD算子

Transform算子:生成一个新的RDD

map/filter/flatMap/groupByKey/reduceByKey/..

image.png

Action算子:触发Job提交

collect/count/take/saveAsTextFile/.…

image.png

image.png

2.2.3 RDD依赖

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

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

  • NarrowDependency

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

  • One ToOneDependency

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

  • RangeDependency

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

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

List(partitionld - outStart + inStart)

  • PruneDependency

image.png

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

  • ShuffleDependency

image.png

2.2.4 RDD执行流程

Task: Stage 内执行单个partition 任务

Stage: 依据宽依赖划分

Job: RDD action算子触发

image.png

2.3 Scheduler

image.png

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

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

2.4 Memory Management

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

  • UnifiedMemoryManager 统一管理 Storage/Execution 内存

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

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

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

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

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

  • spark.memory.storageFraction边界

image.png

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

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

image.png

2.5 Shuffle

spark.shuffle.manager -> sort


trait ShuffleManager {

def registerShuffle..

def getWriter..

def getReader..

def unregisterShuffle.…



class SortShuffleManager extends ShuffleManager

image.png

SortShuffleManager

image.png

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

dataFile中的数据按照partitionld进行排序,同一个partitionld的数据聚集在一起

indexFile保存了所有paritionld在dataFlle中的位置信息,方便后续ReduceTask能Fetch到对应partitionld 的数据

image.png

External Shuffle Service + Dynamic Resource Allocation(DRA)

image.png

fetch partition-1 from MapTask-1's shuffle dataFile

image.png

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

03.SparkSQL原理解析

image.png

image.png

3.1 Catalyst优化器

image.png

RBO

Rule Based Optimizer(RBO)

image.png

Batch执行策略:

Once->只执行一次

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

image.png

ConstantFolding/PushDownPredicates/ColumnPruning

/DynamicFilterPruning/SimplifyConditionals/..

object PushDownPredicates extends Rule {

def apply(plan: LogicalPlan): LogicalPlan = plan transformDown {

...

}

}

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

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

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

image.png

Cost Based Optimizer(CBO)

采集表的statistics

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

image.png

image.png

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

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

JoinReorder

image.png

JoinSelection

Broadcast Hash Join(BHJ)

Shuffle Hash Join(SHJ)

Sort Merge Join(SMJ)

3.2 Adaptive Query Execution(AQE)

AQE

image.png

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

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

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

目前支持的优化场景:

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

  • SMJ -> BHJ

  • Skew Join优化

Coalescing Shuffle Partitions

Partition 合并(coalescing shuffle partitions)

image.png 合并后:

image.png

问题

spark.sql.shufle.partition作业粒度参数,一个作业中所有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

Switching Join Strategies

SortMergeJoin (SMJ) -> BroadcastHashJoin (BHJ)

image.png

问题:

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

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

Optimizing Skew Joins

Skew Join

![](vx_images/499293018253395.png =400x)

![](vx_images/1803118239566.png =400x)

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

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


spark.sql.adaptive.skewJoin.enabled

spark.sql.adaptive.skewJoin.skewedPartitionFactor

spark.sql.adaptive.skewJoin.skewedPartitionThresholdlnBytes

3.3 Runtime Filter

select x,y from A join B on A.a = B.b

3.4 Bloom Runtime Filter

链接

tpcds/q16.sql:

AND cs1.cs_call_center_sk

= cc_call_center_sk

image.png

image.png

3.5 Codegen

Expression

表达式(Expression)

image.png

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

image.png

image.png

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

WholeStageCodegen

算子/WholeStageCodegen

select (a+1) * a from t where a = 1

火山模型(Volcano)

image.png

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

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

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

image.png

image.png

一个SQL包含多个Stage的WholeStageCodegen

image.png

04.业界挑战与实践

4.1 Shuffle稳定性问题

Shuffle稳定性问题

image.png

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

Shuffle稳定性解决方案

image.png

4.2 SQL执行性能问题

SQL执行性能问题

image.png

  • 压榨CPU资源

CPU流水线/分支预测/乱序执行

/SIMD/CPU缓存友好/.…

Vectorized / Codegen ?

C++ / Java?

SQL执行性能解决方向

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

image.png

Intel: OAP/gazelle_plugin

github.com/oap-project…

image.png

4.3 参数推荐/作业诊断

Spark参数很多,资源类/Shuffle/Join/Agg/.…

调参难度大

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

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

自动参数推荐/作业诊断