spark中的持久化机制以及lineage和checkpoint(简含源码解析)
spark相比MapReduce最大的优势是,spark是基于内存的计算模型,有的spark应用比较复杂,如果中间出错了,那么只能根据lineage从头开始计算,所以为了避免这种情况,spark提供了两种持久化算子,如果用术语回答,持久化的目的就是为了容错。
存储级别
spark贴心地提供了多种存储级别供选择

这里解释一下StorageLevel的参数是什么
1._useDisk
2._useMemory
3._useOffHeap
4._deserialized
5._replication 默认值为1
里解释一下什么是OffHeap,我们知道一般java中对象是分配到jvm中的堆中,而OffHeap就是堆外内存,这片内存直接被操作系统管理而不是受jvm管理,这样的话可以减少GC的影响。
再解释下deserialized,是serialized的反过程,序列化和反序列化指得是对象与字节码文件间转换的过程。
如何选择存储级别
java、scala中的存储级别可以认为是DISK, MEMORY, SERIABLIZED和副本数之间排列组合,如何进行选择在官方文档上(文档地址可以直接搜spark或者去我上篇RDD算子介绍的博客中进入),我这里直接对原文进行硬翻
如果使用默认的存储级别(MEMORY_ONLY),存储在内存中的 RDD 没有发生溢出,那么就选择默认的存储级别。默认存储级别可以最大程度的提高 CPU 的效率,可以使在 RDD 上的操作以最快的速度运行
如果内存不能全部存储 RDD,那么使用 MEMORY_ONLY_SER,并挑选一个快速序列化库将对象序列化,以节省内存空间。使用这种存储级别,计算速度仍然很快
除了在计算该数据集的代价特别高,或者在需要过滤大量数据的情况下,尽量不要将溢出的数据存储到磁盘。因为,重新计算这个数据分区的耗时与从磁盘读取这些数据的耗时差不多。
如果想快速还原故障,建议使用多副本存储级别(例如,使用 Spark 作为 web 应用的后台服务,在服务出故障时需要快速恢复的场景下)。所有的存储级别都通过重新计算丢失的数据的方式,提供了完全容错机制。但是多副本级别在发生数据丢失时,不需要重新计算对应的数据库,可以让任务继续运行。
cache和persist算子的区别

可以看出,cache就是persist的偷懒版本,persist可以自定义StorageLevel,但是cache就是默认的MEMORY_ONLY
观察persist的源码


源码本身没什么好讲的,除了那个localcheckPointed,这个可以按下不表,在spark持久化中还有两个重要的概念,
checkpoint, lineage
checkpoint和lineage
checkpoint和lineage都是RDD容错的手段
lineage相当于族谱,记录了RDD变化的过程,这一点与mysql的redo log差不多,在发生错误的时候,通过lineage来重新运算获取数据,lineage描述的是RDD的变化链,有两种路径,一种是窄依赖,另一种是宽依赖,这两种依赖可以想像为独生和超生,一个父只对应一个孩子是窄依赖,一个父对应多个孩子叫做宽依赖。
checkpoint机制是来弥补lineage中宽依赖带来的冗余计算问题(理想中一个孩子丢了只需要“生”出丢失的孩子即可,但是实操如果要复原,只能父重新计算出所有孩子,带来了计算冗余”),对这种宽依赖或者说触发shuffle的算子加checkpoint是可以考虑的事情。checkpoint就是将数据导入可靠的具有容错的存储系统中,一般指定为hdfs,也可以是s3之类的,不过这样做的话会斩断依赖链

重要的代码也就是最后的赋值,这行代码的意思是标记这个RDD需要checkpointing,但是这个并没有立即执行,需要后面action触发才能执行,也就是

中的最后一行(runJob函数是每个action算子中必定执行的函数)

这段代码的意思一个RDD初始时doCheckPointCalled为false,经过一轮后为true,checkpointData.isDefined为true,则说明rdd已经被checkPoint过了,则真正执行进行checkpointData的真实checkpoint()操作,而上面的checkpointAllMarkedAncestors表示此RDD的祖先被checkpoint都通过这个job触发处理,默认是false,也就是说

这个时候只会触发RDD4的checkpoint转存,而不会触发RDD1的转存
下面是真正执行checkpoint操作的函数

首先把cpState(RDDCheckpointData中的一个属性)置为CheckpointingInProgress,然后进行doCheckPoint操作产生newRDD,这里的doCheckPoint操作并没有实现,那么就去寻找它的子类,这时应该想起

这里,ReliableRDDCheckpointData,这个就是RDDCheckPoint的子类,里面实现doCheckpoint操作

很明显,最重要的代码是 ReliableCheckpointRDD.writeRDDToCheckpointDirectory方法,下面给出它的代码,太长了就不截图了
点击查看代码
def writeRDDToCheckpointDirectory[T: ClassTag](
originalRDD: RDD[T],
checkpointDir: String,
blockSize: Int = -1): ReliableCheckpointRDD[T] = {
val checkpointStartTimeNs = System.nanoTime()
val sc = originalRDD.sparkContext
// Create the output path for the checkpoint
val checkpointDirPath = new Path(checkpointDir)
val fs = checkpointDirPath.getFileSystem(sc.hadoopConfiguration)
if (!fs.mkdirs(checkpointDirPath)) {
throw new SparkException(s"Failed to create checkpoint path $checkpointDirPath")
}
// Save to file, and reload it as an RDD
val broadcastedConf = sc.broadcast(
new SerializableConfiguration(sc.hadoopConfiguration))
// TODO: This is expensive because it computes the RDD again unnecessarily (SPARK-8582)
sc.runJob(originalRDD,
writePartitionToCheckpointFile[T](checkpointDirPath.toString, broadcastedConf) _)
if (originalRDD.partitioner.nonEmpty) {
writePartitionerToCheckpointDir(sc, originalRDD.partitioner.get, checkpointDirPath)
}
val checkpointDurationMs =
TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - checkpointStartTimeNs)
logInfo(s"Checkpointing took $checkpointDurationMs ms.")
val newRDD = new ReliableCheckpointRDD[T](
sc, checkpointDirPath.toString, originalRDD.partitioner)
if (newRDD.partitions.length != originalRDD.partitions.length) {
throw new SparkException(
"Checkpoint RDD has a different number of partitions from original RDD. Original " +
s"RDD [ID: ${originalRDD.id}, num of partitions: ${originalRDD.partitions.length}]; " +
s"Checkpoint RDD [ID: ${newRDD.id}, num of partitions: " +
s"${newRDD.partitions.length}].")
}
newRDD
}
这个函数的作用就是将rdd写入checkpointDir中(其实是hdfs中一个路径),然后生产newRDD,newRDD就是以写入hdfs目录中的rdd数据生产的rdd
最后执行下半程序,更新rdd并斩断lineage,斩断也就是将rdd中dependency属性置为null,整个过程就结束了

总结就是,RDD在进行checkPoint操作的时候先进行标记,然后直到action算子中的runJob函数里的docheckPoint才真正执行checkpoint操作,而具体如何checkpoint,依靠子类ReliableRDDCheckpointData中重写的checkpoint函数进行更新和斩断,更新rdd根据写入hdfs中的rdd数据进行生成,而斩断就是将生成的rdd的dependency属性置位null
spark中的持久化机制以及lineage和checkpoint(简含源码解析)的更多相关文章
- SpringtMVC中配置 <mvc:annotation-driven/> 与 <mvc:default-servlet-handler/> 的作用与源码解析
基于 Spring4.X 来学习 SpringtMVC, 在学习过程中,被"告知"在 XML 配置文件中建议设置如下两项: 一直不明白为什么,但又甘心.于是,花了一点时间来调试源码 ...
- [源码解析] 深度学习分布式训练框架 horovod (8) --- on spark
[源码解析] 深度学习分布式训练框架 horovod (8) --- on spark 目录 [源码解析] 深度学习分布式训练框架 horovod (8) --- on spark 0x00 摘要 0 ...
- [Spark內核] 第42课:Spark Broadcast内幕解密:Broadcast运行机制彻底解密、Broadcast源码解析、Broadcast最佳实践
本课主题 Broadcast 运行原理图 Broadcast 源码解析 Broadcast 运行原理图 Broadcast 就是将数据从一个节点发送到其他的节点上; 例如 Driver 上有一张表,而 ...
- Scala 深入浅出实战经典 第65讲:Scala中隐式转换内幕揭秘、最佳实践及其在Spark中的应用源码解析
王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...
- Scala 深入浅出实战经典 第61讲:Scala中隐式参数与隐式转换的联合使用实战详解及其在Spark中的应用源码解析
王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载: 百度云盘:http://pan.baidu.com/s/1c0noOt ...
- Scala 深入浅出实战经典 第60讲:Scala中隐式参数实战详解以及在Spark中的应用源码解析
王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...
- Scala 深入浅出实战经典 第48讲:Scala类型约束代码实战及其在Spark中的应用源码解析
王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-64讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...
- Spark 源码解析 : DAGScheduler中的DAG划分与提交
一.Spark 运行架构 Spark 运行架构如下图: 各个RDD之间存在着依赖关系,这些依赖关系形成有向无环图DAG,DAGScheduler对这些依赖关系形成的DAG,进行Stage划分,划分的规 ...
- Spark Broadcast内幕解密:Broadcast运行机制彻底解密、Broadcast源码解析、Broadcast最佳实践
本课主题 Broadcast 运行原理图 Broadcast 源码解析 Broadcast 运行原理图 Broadcast 就是将数据从一个节点发送到其他的节点上; 例如 Driver 上有一张表,而 ...
- [源码解析] 从TimeoutException看Flink的心跳机制
[源码解析] 从TimeoutException看Flink的心跳机制 目录 [源码解析] 从TimeoutException看Flink的心跳机制 0x00 摘要 0x01 缘由 0x02 背景概念 ...
随机推荐
- vue项目中axios跨域设置
最近项目中遇到一个问题,测试环境和线上环境需要调同一个接口(接口地址是线上的),本地开发的时候遇到了跨域的问题,刚开始用了fetch解决的,代码如下 方法一 step1:安装包node-fetch,然 ...
- docker安装常用软件
linux安装docker 1.安装gcc相关 yum install gcc -y yum install gcc-c++ -y 2.安装工具包 #安装工具包 yum -y install yum- ...
- Linux系统管理实战-配置静态IP
配置静态IP 前置条件 防火墙: EL7 EL6 查看状态: # systemctl status firewalld # /etc/init.d/iptables status 立即关闭: # sy ...
- python发布定时任务(schedule模块)
最近公司开始让在自己开发的软件上写日志,有几次下班了都忘了写,好几次都是给领导发邮件,说明一下自己没有写日志这件事,很麻烦.但是我一想我每天都要写,岂不是很麻烦,想想自己之前有爬虫经验,为什么自己不写 ...
- dubbo相关面试题
1.说说Dubbo的分层? 从大的范围来说,dubbo分为三层,business业务逻辑层由我们自己来提供接口和实现还有一些配置信息,RPC层就是真正的RPC调用的核心层,封装整个RPC的调用过程.负 ...
- 01.数据库基础、JDBC
一.数据库 数据库:用于存储和管理数据的仓库. 数据库的特点 持久化储存数据,数据库就是一个文件系统. 方便储存和管理数据. 使用了统一的方式操作数据库--SQL. 配置 Mysql 服务启动 手动 ...
- toLua文件夹结构
写在前面 本文是我对toLua(1.0.8.591版本)文件夹内容理解的记录. 文件夹结构 总览 下图是toLua的Unity工程视图: BaseType 基础类型的Wrap文件,有些是自动生成(即用 ...
- 8、HTTP Cookie管理器
如果有需要加的cookie 就选择添加 如果没有特殊的 就默认就好了 第一次访问是没有cookie的 会话 session Cookie 是明文的 Session id 是保存在cookie里 ...
- 【原创】freetype android交叉编译
项目中Opencv需要显示中文,由于本身并不支持,所以需要借助第三方的库freetype来实现.这个库虽然android本身也有使用,但并没有暴露接口给外部使用. freetype官网 方式1 脚本编 ...
- 9.6 2020 实验 1:Mininet 源码安装和可视化拓扑工具
一.实验目的 掌握 Mininet 的源码安装方法和 miniedit 可视化拓扑生成工具. 二.实验任务 使用源码安装 Mininet 的 2.3.0d6 版本,并使用可视化拓扑工具生成一个最简 ...