这是我参与「第四届青训营 」笔记创作活动的第9天
spark是一个优秀的批处理计算引擎,能够高效率,高容错的完成离线型计算任务。
spark架构
- 统一引擎,支持多种分布式场景
- 多语言支持
- 可读写丰富数据源
- 丰富灵活的 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是对用户数据和行为的代理,等等