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

125 阅读5分钟

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

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

一、本堂课重点内容

  1. 大数据处理常见链路,Spark 特点

  2. SparkCore 中核心概念 RDD 调度机制,内存管理机制,shuffle 机制

  3. SQL 在 Spark 引擎中执行的详细流程及优化方案

  4. 了解目前业界遇到的挑战,解决方案

二、详细知识点介绍

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

1.1 大数据处理技术栈

数据,存储,计算,应用。

1.3 开源大数据处理引擎

Batch: Hadoop、Hive、Spark

Streaming: Flink

OLAP: presto、ClinckHouse

1.4 什么是 Spark?

47.JPG

图源:教学PPT

—— Spark 生态 & 特点

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

  • 多语言支持

  • 可读写丰富数据源

内置 DataSource、自定义 DataSource

  • 丰富灵活的 API/算子

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

1.5 Spark 特点

—— 多语言支持

SQL、Java/Scala、R、Python

—— 丰富数据源

内置 DataSource、自定义 DataSource

—— 丰富的 API/算子

SparkCore -> RDD

SparkSQL -> DataFrame

1.6 Spark 运行架构 & 部署方式

48.JPG

图源:教学PPT

1.10 提交一个简单任务

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

1.11 Spark UI

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

Job 运行完 -> SparkHistoryServer 可查看

2. SparkCore 原理解析

2.1 SparkCore

RDD、资源管理、DAG/Task 调度、内存管理、Shuffle...

49.JPG

图源:教学PPT

2.2 什么是 RDD ?

RDD(Resilient Distributed Dataset)

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 的五要素:

PartitionsA list of partitions
ComputeA function for computing each split
DependenciesA list of dependencies on other RDDs
Partitionera Partitioner for key-value RDDs(e.g. to say that the RDD is hash-paaetitioned)
PreferredLocationsa list of preferred locations to compute each split on (e.g. block locations for an HDFS file

2.2.1 如何创建 RDD?

  • 内置 RDD

  • 自定义 RDD

class CustomRDD(...) extends RDD{}

2.2.2 RDD 算子

  • Transform 算子:生成一个新的 RDD

  • Action 算子:触发 Job 提交

2.2.3 RDD 依赖

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

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

    • NarrowDependency

    • OneToOneDependency

    • RangeDependency

    • PruneDependency

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

    • ShuffleDependency

2.2.4 RDD 执行流程

50.JPG

图源:教学PPT

2.3 调度器

根据 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 内存管理

  • Executor 内存主要有两类:Storage、Execution
  • UnifiedMemoryManager 统一管理 Storage/Execution 内存
  • Storage 和 Execution 内存使用是动态调整,可以相互借用
  • 当 Storage 空闲,Execution可以借用 Storage 的内存使用,可以减少 spill 等操作,Execution 使用的内存不能被 Storage 驱逐
  • 当 Execution 空闲,Storage 可以借用 Execution 的内存使用,当 Execution 需要内存时,可以驱逐被 Storage 借用的内存,直到 spark.memory.storageFraction 边界

2.4.1 多任务间内存分配

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

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

2.5 Shuffle

trait ShuffleManager{
  def registerShuffle...
  def getWriter...
  def getReader...
  def unregisterShuffle...
}

class SortShuffleManager extends ShuffleManager

2.5.1 SortShuffleManager

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

2.5.2 External Shuffle Service

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

3. SparkSQL 原理解析

55.JPG

图源:教学PPT

3.1 Catalyst 优化器

—— RBO

Rule Based Optimizer

Batch 执行策略:

  • Once -> 只执行一次

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

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

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

—— CBO

Cost Based Optimizer

TableStat 从 ANALYZE TABLE 获取

后续的算子的 Stat 通过对应的 Estimation 进行估算

3.2 Adaptive Query Execution(AQE)

目前支持的优化场景:

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

  • SMJ -> BHJ

  • Skew Join 优化

—— Coalescing Shuffle Partitions

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

—— Switching Join Strategies

SortMergeJoin(SMJ) -> BroadcastHashJoin(BHJ)

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

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

—— Optimizing Skew Joins

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

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

3.3 Runtime Filter

Runtime Filter 减少了大表的扫描,shuffle 的数据量以及参加 Join 的数据量,所以对整个集群 IO/网络/CPU 有比较大的节省(10TB TPC-DS 获得了大约35%的改进)

4. 业界挑战与实践

4.1 Shuffle 稳定性问题

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

—— 稳定性解决方案

CompanyDescription
Ubergithub.com/uber/Remote…
LinkedlnMagnet: Push-based Shuffle Service for Large-scale Data Processing
Alibabagithub.com/alibaba/Rem…
Tencentgithub.com/Tencent/Fir…
Bytedance详见课程->大数据 Shuffle 从入门到精通

4.2 SQL 执行性能问题

压榨 CPU 资源

4.3 参数推荐/作业诊断

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

调参难度大

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

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

-> 自动参数推荐/作业诊断