之前我们介绍了Spark的RDD,transformations和actions的概念,这一节我们来看看Spark是如何实现弹性计算的,也就是在集群中的机器出现故障时,保证计算完成的。
MapReduce的弹性计算
我们先来回顾一下MapReduce实现弹性计算的基础,主要有以下两点:
- 使用HDFS存储输入和输出文件,利用HDFS的高可用保证数据的安全;
- 保证mapper和reducer的执行都是确定性的,并且是没有负效应的。

Spark的弹性计算
如何想实现Spark的弹性计算,首先需要满足一个基本条件,就是所有的计算过程是确定性的,并且没有负效应。这不仅仅针对transformation,也针对传入的闭包。
确定性指的是每次调用同一个方法,能得到相同的返回值;没有负效应指的是调用的方法不能改变方法以外的内容,例如修改数据库的操作,在Spark程序执行出错时是无法回滚的。
Transformations的重试
在满足以上条件后,Spark允许在执行失败时进行重试。为了确定需要重新执行哪些任务,Spark会记录任务之间的血缘关系,并组成依赖关系图,其中包括了所有参与计算的RDD的所有分区。

机器故障可能导致图中的某一阶段执行失败。首先我们要检测出具体哪个分区执行失败。这是由Driver负责的,因为Driver会管理整个计算过程,并跟踪每个分区的状态。在找到失败的分区后,继续查找它的依赖。如果它的依赖也失败了,同样需要重启,直到找到最初的RDD,它的数据是存放在可靠的存储上的。

对于宽依赖来说,每个分区会依赖上一级所有分区的数据。因此如果任意一个依赖分区失败,都需要重新计算这一阶段的所有分区数据。

Actions的重试
Spark中的actions操作通常都是有负效应的,Spark并不能保证exactly once的语义,因此actions的重试会导致代码执行多次。这里需要保证actions的负效应的代码满足幂等性的条件,也就是执行多次得到相同的结果。
比如collect算子,产生一个不可变的集合,当重新执行时生成新的集合;还有saveAsTextFile算子,即使多次写入,文件也是不变的。
小结
Spark的弹性计算基础是生成计算的依赖关系图,并假设transformations的调用是确定性的,并且没有负效应的;actions的调用是满足幂等性的。
网友评论