05-Spark原理与实践 | 青训营笔记
这是我参与「第四届青训营 」笔记创作活动的第2天
01-大数据处理引擎Spark介绍
1.1 大数据处理技术栈
数据 -> 存储 -> 计算 -> 应用
海量的数据,数据源种类丰富多样,快速处理来提升数据的价值。
存储:Parquet/ORC高性能列式存储,数据湖技术优化存储的读写
HDFS: 分布式文件存储系统
Kafka: 分布式消息系统
HBase: k-v分布式存储的No-SQL引擎数据库
Kudu: 基于列式存储的分布式数据库
TOS/S3: 对象存储
Presto/Implala/ClickHouse: 处理在线分布式查询场景的,可交互式的OLAP引擎
常见的大数据处理链路:
-
数据源(数据库,日志,…)
-
数据采集(采集到的原始数据可以存储到:Kafka,HDFS,…)
-
数据处理(Kafka,Iceberg/Hudi;多次数据的读取和写入)
-
数据应用(BI报表,OLAP,机器学习)
Spark特点:
- 统一引擎,支持多种分布式场景
- 多语言支持
- 可读写丰富数据源
- 丰富灵活的 API/算子
- 支持 K8S/YARN/Mesos 资源调度
02-SparkCore原理介绍
RDD:表示可以并行操作的不可变的、分区的元素集合。
两类RDD算子:
- Transform算子:生成一个新的RDD
- Action算子:触发Job提交
RDD依赖:
窄依赖:父RDD的每个partition至多对应一个子RDD分区。
宽依赖(会产生Shuffle):父RDD的每个partition都可能对应多个子RDD分区。
RDD执行流程:
- Job:RDD action算子触发
- Stage:依据宽依赖划分
- Task:Stage内执行单个partition任务
根据ShuffleDependency切分Stage,并按照依赖顺序调度Stage,为每个Stage生成并提交TaskSet到TaskScheduler
根据调度算法(FIFO/FAIR)对多个TaskSet进行调度,对于调度到的TaskSet,会将Task调度(locality)到相关Executor_上面执行,Executor SchedulerBackend提供。
Executor内存主要有两类:Storage、Execution
UnifiedMemoryManager统一管理Storage/Execution内存
Storage和Execution内存使用是动态调整,可以相互借用
当Storage空闲,Execution可以借用Storage的内存使用,可以减少spill等操作,Execution使用的内存不能被Storage驱逐
当Execution空闲,Storage可以借用Execution的内存使用,当Execution需要内存时,可以驱逐被Storage借用的内存,直到spark.memory.storageFraction边界
03-SparkSQL原理解析
RBO使用Catalyst优化器
Adaptive Query Execution(AQE):每个Task结束会发送MapStatus信息给Driver。Task的MapStatus中包含当前Task Shuffle产生的每个Partition的size统计信息。Driver获取到执行完的Stages的MapStatus信息之后,按照MapStatus中partition;大小信息识别匹配一些优化场景,然后对后续未执行的Plan进行优化。
04-业界挑战与实践
- shuffle稳定性问题
在大规模作业下,开源ExternalShuffleService(ESS)的实现机制容易带来大量随机读导致的磁盘IOPS瓶颈、Fetch请求积压等问题,进而导致运算过程中经常会出现Stage重算甚至作业失败,继而引起资源使用的恶性循环,严重影响SLA。
远程shuffle方案来解决。
- SQL执行性能问题
- SQL执行性能解决方向
- 参数推荐/作业诊断