Overview
- 基于内存的迭代计算框架
- 不适用:
- 内存不足的情况下,会flush到磁盘导致性能急剧下降,甚至可能fail。
- 有非常高实时性要求的流式计算业务 (spark streaming 底层还是batch)
架构(Spark on Yarn)

- Spark Yarn Client提交作业:向YARN的ResourceManager 申请启动Application Master。
- RM收到请求后,在集群中选择一个Node Manager,为该应用程序分配第一个Container,要求它在这个container中启动AM
- AM向RM进一步申请资源
- 一旦AM申请到资源(container)后,便与对应的NodeManager通信,要求它在获取的Container中启动CoarseGrainedExecutorBackend,CoarseGrainedExecutorBackend启动后会向spark context注册并申请task
- CoarseGrainedExecutorBackend运行task并向driver汇报运行的状态和进度,从而可以在任务失败时重启任务。
编程模型-RDD

- Spark核心--> 数据抽象:RDD弹性分布式数据集。一个容错的,并行的数据结构。本质上是一个只读(immutable)的分区记录集合。
- 从功能上说,RDD可以让用户显式地将数据存储到磁盘和内存中,并能控制数据的分区。
- 特性总结:
- immutable
- 支持跨集群的分布式数据结构
- 可以根据记录的key对结构进行分区 (partition)
- 提供了粗粒度的操作,且这些操作都支持分区
- 数据存储在内存中,从而提供低延迟
宽窄依赖
- RDD之间可以相互依赖。
- 如果RDD的分区最多只能被一个Child RDD的一个分区使用,就称为narrow dependency
- 若多个child rdd都可以依赖,则称之为wide dependency。(主要就是看当前rdd是否会流向多个rdd)
- 区分的原因:
- narrow dependency可以支持在同一个cluster node上以pipeline管道形式执行。
- 第二个是从失败恢复考虑。narrow dependency的失败恢复更有效,因为它只需要计算丢失的parent partition即可,而且可以并行地在不同节点进行重计算。[这个计算对数据的利用是100%的]
容错
- 通常来说,大部分系统采用的容错方式有两种:数据复制 or 日志记录。 然而对以数据为中心的系统而言,这两种方式都非常昂贵,因为它需要跨集群网络拷贝大量数据。
- 对分布式数据集的容错,一般有两种方式:数据检查点和记录数据的更新。
- 然而面向大规模数据分析,数据检查点操作成本很高,需要通过数据中心的网络连接在机器之间复制庞大的数据集。
- 背景:RDD天生是支持容错的。
- 首先,它是一个immutable的数据集。
- 其次,它能够记住构建它的操作图(Graph of operation)
- 并且,RDD只支持粗粒度转换。
因此当执行任务的worker失败时,完全可以通过dag来重新计算。
--> 由于无需采用replication方式容错,很好地降低了跨网络的数据传输成本。
- 以上称为 基于lineage的容错方式。lineage本质上很类似于数据库中的重做日志(redo log),只不过它的粒度很大。
- 然而,在某些场景下,Spark也需要利用记录日志的方式来支持容错。也就是spark的 checkpointing 机制啦。
Checkpointing机制
- RDD需要加检查点的情况:
- DAG中lineage过长,如果重算,则开销太大
- 在宽依赖上做checkpointing获得收益更大。
- 由于RDD是只读的,所以Spark的RDD计算中一致性不是主要关心的内容,内存相对容易管理。[这也是设计者很有远见之处,减少了框架的复杂性,提升了性能和可扩展性。]
- 检查点(本质是通过将RDD写入Disk做检查点)是为了通过lineage做容错的辅助。
- 对Spark Streaming而言,它需要7*24,所以需要checkpointing足够多信息来做到失败恢复。SS会checkpointing两种类型的数据:
- Metadata checkpointing:保存定义了Streaming计算逻辑至类似HDFS的支持容错的存储系统,用来恢复driver。元数据包括:
- 配置:用于创建该streaming Application的所有配置
- DStream操作
- 未完成的batches:那些提交了job但尚未执行或为完成的batches
- Data checkpointing:保存已生成的RDDs至可靠的存储。这在某些stateful转换中是需要的,在这种转换中,生成RDD需要依赖前面的batches。
- Metadata checkpointing:保存定义了Streaming计算逻辑至类似HDFS的支持容错的存储系统,用来恢复driver。元数据包括:
- checkpointing的局限:checkpointing刷到外部存储的是类Checkpoint对象序列化后的数据。那么在Spark Streaming Application重新编译后,再去反序列化checkpointing的数据就会失败。这个时候就必须新建SparkContext。
Failover
内存管理
DataFrame
- DF是一种以RDD为基础的分布式数据集,类似于传统数据库中的二维表格
- DF带有schema元数据,即DF所表示的二维表数据集的每一列都带有名称和类型
Spark VS Hadoop
Shuffle
- high level上来看,两者没有太大的区别:
- 将mapper(Spark里是 ShuffleMapTask)的输出进行partition
- 不同的partition送到不同的reducer (Spark里的reducer可能是下一个stage里的ShuffleMapTask,也可能是ResultTask)
- Reducer以内存做缓存区,边shuffle边aggregate数据,等到数据aggregate好以后进行reduce (Spark里可能是后续的一系列的操作)
- 但是在low level看,两者差别不小。
- spark shuffle 默认是 hash-based,hadoop shuffle 是 sorted-based。 (如果用户需要sorted的数据,需要用户自己调用类似 sortByKey()的操作)
- 实现角度来看,也有不少差别
- MR shuffle有几个明显的阶段:map(), spill, merge, shuffle, sort, reduce
- Spark 中没有这样功能明确的阶段,只有不同的stage 和 一系列的transformation()
- spark shuffle 是算子驱动的,主要有两个阶段
- shuffle write (ShuffleMapTask)
- 将map的结果文件中的数据记录送到对应的bucket(缓冲区)内 [本质就是一个hash过程]
- 之后,每个bucket内的数据会不断被写到本地磁盘,形成一个ShuffleBlockFile,简称FileSegment。
这样实现的问题:- 产生的FileSegment过多。M个ShuffleMapTask就会产生 M * R 个FileSegment。
这个问题目前有一些方法:- Spark中已实现的FileConsolidation
![]()
- 在一个core上连续执行的ShuffleMapTasks可以共用一个输出文件ShuffleFile
- 先执行完的ShuffleMapTask形成ShuffleBlock i,后执行的ShuffleMapTask直接将数据append到i后面。组成一整个FIleSegment
- 这样每个worker持有文件数将为cores * R
- Spark中已实现的FileConsolidation
- 缓存区占用内存空间过大。
- 产生的FileSegment过多。M个ShuffleMapTask就会产生 M * R 个FileSegment。
- shuffle read
- 什么时候fetch?(是parent stage中的一个ShuffleMapTask执行完,还是等全部ShuffleMapTask执行完?)
--> 当parent stage的所有ShuffleMapTasks结束后再fetch。
理论上讲,一个ShuffleMapTask结束后就可以fetch,但是为了迎合stage的概念(即一个stage如果其parent stages没有执行完,自己是不能被提交执行的)。 - 边fetch边处理,还是fetch完再处理? (对MR而言,是边fetch边使用combine()进行处理,但reduce()要等到全部数据都shuffle-sort后才能开始)
--> spark shuffle可以边fetch边reduce处理。不过spark想要实现边fetch边处理,需要满足条件:1. 流入的records可以无序; 2. 可以用aggregate的数据结构,比如HashMap。 - fetch来的数据存放到哪里?
--> 刚fetch来的FileSegment存放在sortBuffer缓冲区,经过处理后的数据放在内幕才能+磁盘(这是可设置的,比如spark.shuffle.spill=false就只用内存)上。 - 怎么获取fetch的数据的存放位置?
--> 每个ShuffleMapTask完成时会将FileSegment的存储位置汇报给MapOutputTrackerMaster。
- 什么时候fetch?(是parent stage中的一个ShuffleMapTask执行完,还是等全部ShuffleMapTask执行完?)
- shuffle write (ShuffleMapTask)
- spark shuffle 是算子驱动的,主要有两个阶段
- hash-based vs sorted-based
Spark Shuffle
- 相比MR固定的shuffle-combine-merge-reduce策略,Spark更加灵活,会根据transformation()的语义去设计不同的shuffle-aggregate策略,再加上不同的内存数据结构来混搭出合理的执行流程。
- 接下来,我们看一些算子是怎么做shuffle的。重点在于Spark是如何在不排序records的情况下完成shuffle write和shuffle read,以及怎么将shuffle过程融入RDD computing chain中的。
- TODO: 还没看完:https://github.com/JerryLead/SparkInternals/blob/master/markdown/4-shuffleDetails.md
# TODO: read 这篇blog。
FYI
满地都是六便士,她却抬头看见了月亮。

浙公网安备 33010602011771号