spark中的容错

来源:互联网 发布:mysql 名字的格式 编辑:程序博客网 时间:2024/06/16 03:18

一般来说,分布式数据集的容错性有两种方式:数据检查点和记录数据的更新。面向大规模数据分析,数据检查点操作成本很高,需要通过数据中心的网络连接在机器之间复制庞大的数据集,而网络带宽往往比内存带宽低得多,同时还需要消耗更多的存储资源。因此,Spark选择记录更新的方式。但是,如果更新粒度太
细太多,那么记录更新成本也不低。因此,RDD只支持粗粒度转换,即在大量记录上执行的单个操作。将创建RDD的一系列Lineage(即血统)记录下来,以便恢复丢失的分区。Lineage本质上很类似于数据库中的重做日志(Redo Log),只不过这个重做日志粒度很大,是对全局数据做同样的重做进而恢复数据。

Lineage

相比其他系统的细颗粒度的内存数据更新级别的备份或者LOG机制,RDD的Lineage记录的是粗颗粒度的特定数据Transformation操作(如filter、map、join等)行为。当这个RDD的部分分区数据丢失时,它可以通过Lineage获取足够的信息来重新运算和恢复丢失的数据分区。因为这种粗颗粒的数据模型,限制了Spark的运用场合,所以Spark并不适用于所有高性能要求的场景,但同时相比细颗粒度的数据模型,也带来了性能的提升。

RDD在Lineage依赖方面分为两种:Narrow Dependencies与Shuffle Dependencies,用来解决数据容错的高效性。Narrow Dependencies是指父RDD的每一个分区最多被一个子RDD的分区所用,表现为一个父RDD的分区对应于一个子RDD的分区或多个父RDD的分区对应于一个子RDD的分区,也就是说一个父RDD的一个分区不可能对应一个子RDD的多个分区。Shuffle Dependencies是指子RDD的分区依赖于父RDD的多个分区或所有分区,即存在一个父RDD的一个分区对应一个子RDD的多个分区。
本质理解:根据父RDD分区是对应1个还是多个子RDD分区来区分NarrowDependency(父分区对应一个子分区)和Shuffle Dependency(父分区对应多个子分区)。如果对应多个,则当容错重算分区时,因为父分区数据只有一部分是需要重算子分区的,其余数据重算就造成了冗余计算。

如果一个节点死机了,而且运算Narrow Dependency,则只要把丢失的父RDD分区重算即可,不依赖于其他节点。而Shuffle Dependency需要父RDD的所有分区都存在,重算就很昂贵了。可以这样理解开销的经济与否:在Narrow Dependency中,在子RDD的分区丢失、重算父RDD分区时,父RDD相应分区的所有数据都是子RDD分区的数据,并不存在冗余计算。在Shuffle Dependency情况下,丢失一个子RDD分区重算的每个父RDD的每个分区的所有数据并不是都给丢失的子RDD分区用的,会有一部分数据相当于对应的是
未丢失的子RDD分区中需要的数据,这样就会产生冗余计算开销,这也是ShuffleDependency开销更大的原因。因此如果使用Checkpoint算子来做检查点,不仅要考虑Lineage是否足够长,也要考虑是否有宽依赖,对Shuffle Dependency加Checkpoint是最物有所值的

CheckPoint

以下两种情况下,RDD需要加检查点。
1)DAG中的Lineage过长,如果重算,则开销太大。
2)在Shuffle Dependency上做Checkpoint(检查点)获得的收益更大

由于RDD是只读的,所以Spark的RDD计算中一致性不是主要关心的内容,内存相对容易管理

传统做检查点有两种方式:通过冗余数据和日志记录更新操作。在RDD中的doCheckPoint方法相当于通过冗余数据来缓存数据,而之前介绍的血统就是通过相当粗粒度的记录更新操作来实现容错的

可以通过SparkContext.setCheckPointDir()设置检查点数据的存储路径,进而将数据存储备份,然后Spark删除所有已经做检查点的RDD的祖先RDD依赖。这个操作需要在所有需要对这个RDD所做的操作完成之后再做,因为数据会写入持久化存储造成I/O开销。官方建议,做检查点的RDD最好是在内存中已经缓存的RDD,否则保存这个RDD在持久化的文件中需要重新计算,产生I/O开销