Spark | 青训营笔记

105 阅读4分钟

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

spark是一个优秀的批处理计算引擎,能够高效率,高容错的完成离线型计算任务。

spark架构

image.png

  • 统一引擎,支持多种分布式场景
  • 多语言支持
  • 可读写丰富数据源
  • 丰富灵活的 API/算子
  • 支持 K8S/Mesos 资源调度

spark core

spark可运行的核心在于 spark core 其中包含了 spark 处理和计算的核心理念

RDD

Resilient Distributed Dataset 弹性分布式数据集,表示可并行操作的不可变的分区元素集合。

它是 spark 中最基本的数据处理模型,或者说数据处理单元

五大特性:

  • partitions 分区列表

    每个rdd都有多个分区,每个分区都能运行在不同计算节点上,每一个分区都会被一个task处理,spark分区不指定则默认从集合创建为cpu核数,从文件存储系统创建则为文件的block数

  • compute 计算函数

    spark的计算单位是 partitions ,每一个rdd都会实现一个compute,对具体的 partitions 进行计算

  • dependences 依赖

    RDD会依赖于其他的RDD,也就是RDD每一次处理转换,都会生成一个新的RDD,这里就是类似流式计算的链式编程

  • prititioner 分区

    这里没怎么听懂,老师大致意思是有两种分区列表,依照哈希/range进行分类,其中拥有 key-value 的RDD才会拥有prititioner(主要是没懂这个key-value与前面的哈希/range有什么关系)

  • PerferedLocations 优先位置列表

    存储每一个partition的优先位置,计算每个拆分的首选位置列表(例如HDFS文件的块位置)

调度器

Dag:Directed Acyclic Graph 无回路有向图

RDD的action算子调用 runJob函数 后,就会将自身的 DAG 提交给 DagScheduler

然后依据RDD中dependences分配stage,每一个stage对应生成taskSet集合

taskSet 分发给 taskScheduler,taskSchduler会依据算法对taskSet进行调度

内存管理

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

Shuffle

shuffle指的是对 maptask 与 reducetask 中间存在数据重新分发的这种行为

SparkSQL

这里的东西其实我在第一天就讨论过了,就是 sql-operator 中的内容,这里就不多做赘述

一些思考

我发现一个现象,无论是 sql-operator,Flink,Spark,甚至vue的虚拟节点,他们在优化逻辑与架构底层体现都是类似的:

数据 -> 生成虚拟计划 -> RBO优化 -> 生成物理计划 -> CBO择优 ->代码生成 -> 执行

额,或许有些CBO在物理计划之前

但是这其实都体现出了一种普适性:将所有人的水平拉到同一层次

有的开发者水平很低,因此框架不会信任他们的代码,会对他们代码进行一次又一次的优化,将其拉到高水准的执行效率;

然而高水平的开发者代码或许原本就十分高效,多次的优化重排反而会拉低他们的效率,但是能够预见的是这种代码不具备普适性,只能专一的解决一项事情,甚至可移植性为零

如果对这种逻辑进行再一次的抽象,仅保留对开发者水平的不信任,那么我们的架构又会是什么样呢——如果不信任开发者的水平,那么就让我们对他们的行为进行封装和代理(proxy)

虽说有一点牵强,但是我认为所有的框架都是基于容器对用户行为的代理。sql-operator 是对查询处理顺序的代理,spark,flink 是计算引擎对 task 计算顺序的代理,vue是对数据渲染和节点渲染的代理,spring是对用户数据和行为的代理,等等