spark可以使用checkpoint来作为检查点,将rdd的数据写入hdfs文件,也可以利用本地缓存子系统。

当我们使用checkpoint将rdd保存到hdfs文件时,如果任务的临时文件长时间不删除,长此以往,hdfs会出现很多没有用的文件,spark也考虑到了这一点,因此,用了一些取巧的方式来解决这种问题。

spark config:

spark.cleaner.referenceTracking.cleanCheckpoints = 默认false

也就是说默认情况下,保存的文件一直都会放在dfs中,除非人工删除

下述内容均建立在值为true的情况下

设置检查点路径

spark.sparkContext().setCheckpointDir("hdfs://nameservice1/xx/xx");

存放到hdfs文件系统的好处是自带高容错性、可用性。

那么,所有运行的任务都写这个路径会不会出现覆盖的情况呢?答案是不会

  /**
* Set the directory under which RDDs are going to be checkpointed.
* @param directory path to the directory where checkpoint files will be stored
* (must be HDFS path if running in cluster)
*/
def setCheckpointDir(directory: String) { // If we are running on a cluster, log a warning if the directory is local.
// Otherwise, the driver may attempt to reconstruct the checkpointed RDD from
// its own local file system, which is incorrect because the checkpoint files
// are actually on the executor machines.
if (!isLocal && Utils.nonLocalPaths(directory).isEmpty) {
logWarning("Spark is not running in local mode, therefore the checkpoint directory " +
s"must not be on the local filesystem. Directory '$directory' " +
"appears to be on the local filesystem.")
} checkpointDir = Option(directory).map { dir =>
//利用uuid生成了一个子目录,存放的rdd文件将放到子目录中
val path = new Path(dir, UUID.randomUUID().toString)
val fs = path.getFileSystem(hadoopConfiguration)
fs.mkdirs(path)
fs.getFileStatus(path).getPath.toString
}
}

利用uuid的唯一性,使不同的进程间的checkpoint互不干扰,后续有checkpoint创建的请求时,将会在该目录下创建文件来保存rdd的内容

在生成checkpoint的ReliableRDDCheckpointData 方法中,

保存检查点

  /**
* Materialize this RDD and write its content to a reliable DFS.
* This is called immediately after the first action invoked on this RDD has completed.
*/
protected override def doCheckpoint(): CheckpointRDD[T] = {
//写入到可靠的文件中
val newRDD = ReliableCheckpointRDD.writeRDDToCheckpointDirectory(rdd, cpDir) // Optionally clean our checkpoint files if the reference is out of scope
//默认false,才会注册清理器
if (rdd.conf.get(CLEANER_REFERENCE_TRACKING_CLEAN_CHECKPOINTS)) {
rdd.context.cleaner.foreach { cleaner =>
//注册清理事件
cleaner.registerRDDCheckpointDataForCleanup(newRDD, rdd.id)
}
} logInfo(s"Done checkpointing RDD ${rdd.id} to $cpDir, new parent is RDD ${newRDD.id}")
newRDD
}

注册事件

注册清理事件的意义是当rdd对象无其他引用依赖时,由清理线程异步清理对应的checkpoint文件

  /** Register a RDDCheckpointData for cleanup when it is garbage collected. */
def registerRDDCheckpointDataForCleanup[T](rdd: RDD[_], parentId: Int): Unit = {
registerForCleanup(rdd, CleanCheckpoint(parentId))
} /** Register an object for cleanup. */
private def registerForCleanup(objectForCleanup: AnyRef, task: CleanupTask): Unit = {
referenceBuffer.add(new CleanupTaskWeakReference(task, objectForCleanup, referenceQueue))
}

referenceBuffer的作用是持有CleanupTaskWeakReference对象的引用,防止CleanupTaskWeakReference被提前回收,导致提前清理。

弱引用对象

CleanupTaskWeakReference继承自WeakReference,将referent(也就是rdd),绑定到referenceQueue上,如果gc回收时,发现referent除了referenceQueue这个弱引用外,已经没有其他对象引用,就会将CleanupTaskWeakReference对应放入referenceQueue中

//引用队列,当garbage collector发现对应的可达性改变被发现时,会将引用对象推入队列中
//这是通过Reference.enqueue方法实现的 public boolean enqueue() {return this.queue.enqueue(this);}
private val referenceQueue = new ReferenceQueue[AnyRef] /**
* A WeakReference associated with a CleanupTask.
*
* When the referent object becomes only weakly reachable, the corresponding
* CleanupTaskWeakReference is automatically added to the given reference queue.
*/
private class CleanupTaskWeakReference(
val task: CleanupTask,
referent: AnyRef,
referenceQueue: ReferenceQueue[AnyRef])
extends WeakReference(referent, referenceQueue)

回收线程

再来细致的讲回收线程

在SparkContext初始化时,会启动cleaner,代码较多,直接依次

_cleaner =
if (_conf.get(CLEANER_REFERENCE_TRACKING)) {
Some(new ContextCleaner(this))
} else {
None
}
_cleaner.foreach(_.start())

  /** Start the cleaner. */
def start(): Unit = {
cleaningThread.setDaemon(true) //守护进程
cleaningThread.setName("Spark Context Cleaner")
cleaningThread.start()
//这里有点银弹的意思,定时执行gc,默认半小时一次,主要是应对长时间任务问题
periodicGCService.scheduleAtFixedRate(() => System.gc(),
periodicGCInterval, periodicGCInterval, TimeUnit.SECONDS)
} private val cleaningThread = new Thread() { override def run() { keepCleaning() }}

  /** Keep cleaning RDD, shuffle, and broadcast state. */
private def keepCleaning(): Unit = Utils.tryOrStopSparkContext(sc) {
while (!stopped) {
try {
//从referenceQueue中取可以回收的弱引用对象,弱引用对象返回表示登记的rdd已经可回收了
val reference = Option(referenceQueue.remove(ContextCleaner.REF_QUEUE_POLL_TIMEOUT))
.map(_.asInstanceOf[CleanupTaskWeakReference])
// Synchronize here to avoid being interrupted on stop()
synchronized {
reference.foreach { ref =>
logDebug("Got cleaning task " + ref.task)
//清除强引用
referenceBuffer.remove(ref)
ref.task match {
case CleanRDD(rddId) =>
doCleanupRDD(rddId, blocking = blockOnCleanupTasks)
case CleanShuffle(shuffleId) =>
doCleanupShuffle(shuffleId, blocking = blockOnShuffleCleanupTasks)
case CleanBroadcast(broadcastId) =>
doCleanupBroadcast(broadcastId, blocking = blockOnCleanupTasks)
case CleanAccum(accId) =>
doCleanupAccum(accId, blocking = blockOnCleanupTasks)
case CleanCheckpoint(rddId) =>
doCleanCheckpoint(rddId) //如果任务是cleancheckpoint任务
}
}
}
} catch {
case ie: InterruptedException if stopped => // ignore
case e: Exception => logError("Error in cleaning thread", e)
}
}
}

  /**
* Clean up checkpoint files written to a reliable storage.
* Locally checkpointed files are cleaned up separately through RDD cleanups.
*/
def doCleanCheckpoint(rddId: Int): Unit = {
try {
logDebug("Cleaning rdd checkpoint data " + rddId)
//删除checkpoint操作被触发
ReliableRDDCheckpointData.cleanCheckpoint(sc, rddId)
listeners.asScala.foreach(_.checkpointCleaned(rddId))
logInfo("Cleaned rdd checkpoint data " + rddId)
}
catch {
case e: Exception => logError("Error cleaning rdd checkpoint data " + rddId, e)
}
}

特殊操作的意思

为什么要定时执行System.gc()去触发full gc?

  • 由于删除rdd checkpoint的方法利用了WeakReference,它是一个严重依赖gc的功能,如果没有gc,就不会发现对象可回收,也就不会触发回收逻辑。
  • 极端情况可能出现长时间只有yong gc,而老年区的对象长时间无法回收,而对象早已无其他引用,利用System.gc()来尝试执行full gc,达到回收老年代的目的

总结

  • 默认情况下,保存的文件一直都会放在dfs中,除非人工删除
  • 及时开启spark.cleaner.referenceTracking.cleanCheckpoints,也不能意味着一定能回收,因为垃圾回收并非一定会在合适的时间执行,有可能最终也没有触发弱引用清理任务逻辑

Spark如何删除无效rdd checkpoint的更多相关文章

  1. 大数据入门第二十二天——spark(二)RDD算子(2)与spark其它特性

    一.JdbcRDD与关系型数据库交互 虽然略显鸡肋,但这里还是记录一下(点开JdbcRDD可以看到限制比较死,基本是鸡肋.但好在我们可以通过自定义的JdbcRDD来帮助我们完成与关系型数据库的交互.这 ...

  2. Spark性能调优-RDD算子调优篇(深度好文,面试常问,建议收藏)

    RDD算子调优 不废话,直接进入正题! 1. RDD复用 在对RDD进行算子时,要避免相同的算子和计算逻辑之下对RDD进行重复的计算,如下图所示: 对上图中的RDD计算架构进行修改,得到如下图所示的优 ...

  3. Spark源码分析之Checkpoint的过程

    概述 checkpoint 的机制保证了需要访问重复数据的应用 Spark 的DAG执行图可能很庞大,task 中计算链可能会很长,这时如果 task 中途运行出错,那么 task 的整个需要重算非常 ...

  4. spark第一篇:RDD Programming Guide

    预览 在高层次上,每一个Spark应用(application)都包含一个驱动程序(driver program),该程序运行用户的主函数(main function),并在集群上执行各种并行操作. ...

  5. Spark操作算子本质-RDD的容错

    Spark操作算子本质-RDD的容错spark模式1.standalone master 资源调度 worker2.yarn resourcemanager 资源调度 nodemanager在一个集群 ...

  6. Spark 并行计算模型:RDD

    Spark 允许用户为driver(或主节点)编写运行在计算集群上,并行处理数据的程序.在Spark中,它使用RDDs代表大型的数据集,RDDs是一组不可变的分布式的对象的集合,存储在executor ...

  7. Spark Streaming揭秘 Day8 RDD生命周期研究

    Spark Streaming揭秘 Day8 RDD生命周期研究 今天让我们进一步深入SparkStreaming中RDD的运行机制.从完整的生命周期角度来说,有三个问题是需要解决的: RDD到底是怎 ...

  8. word中几个好用的宏代码(立方米上标、关闭样式自动更新、删除无效样式、表格加粗边框、宋体引号)

    Sub 替换立方米() With Selection.Find .Text = "m3" .Replacement.Text = "mm3" .Forward ...

  9. 【原创】大数据基础之Spark(4)RDD原理及代码解析

    一 简介 spark核心是RDD,官方文档地址:https://spark.apache.org/docs/latest/rdd-programming-guide.html#resilient-di ...

随机推荐

  1. Java 中 static 的作用

    static 关键字的作用 在 Java 中 static 关键字有4种使用场景,下面分别进行介绍: 1.static 成员变量 public class Student { // 静态成员变量 pr ...

  2. robots检测插件编写

    首先先把url分割 url = 'https://www.baidu.com/s?wd=123&rsv_spt=1&rsv_iqid=0x8d22781d000014ad&is ...

  3. Mac 环境下配置 MySQL 以及 Mac终端登录MySQL

    1.首先mysql官网下载Mac 版 mysql直接安装 2.打开偏好设置-> MySQL -> 查看是否开启mysql服务 3.打开终端 进入mysql目录: /usr/local/my ...

  4. Java之再初识二

    本篇博客将继续介绍Java基础知识 1.Java包含哪些数据类型 2.Java基本数据类型转换包含哪两类 3.为什么需要包装类 4.int与integer有什么区别,它们之间如何相互转化的 5.逻辑运 ...

  5. echarts中折线图切换为数据视图(表格布局)表头无法对齐解决方法

                dataView: {               show: true,               readOnly: true,               option ...

  6. POI和easyExcel

    POI与easyExcel 这个东西一般用来做什么? 将用户信息导出为Excel表格(导出数据) 将Excel表中的信息录入到网站数据库(比如一些习题上传) 在开发过程中会遇到对Excel的处理,比如 ...

  7. C++实现职工管理系统(中)

    C++实现职工管理系统(中) 大家好,今天是在博客园的第九天,博主今天给大家带来的是职工管理系统(C++)(中) 这次的随笔记录是实现(上)结语处说的几个功能 目录 C++实现职工管理系统(中) 1. ...

  8. Spring Boot 第三弹,一文带你了解日志如何配置?

    前言 日志通常不会在需求阶段作为一个功能单独提出来,也不会在产品方案中看到它的细节.但是,这丝毫不影响它在任何一个系统中的重要的地位. 今天就来介绍一下Spring Boot中的日志如何配置. Spr ...

  9. 搭建Leanote私有云服务器

    安装流程 安装Golang 安装Leanote 安装Mongodb 配置Leanote 初始化Mongodb数据 运行Leanote 安装Golang # 下载go1.14.4.linux-amd64 ...

  10. JS原生练习

    1.输出1-10000之间的数 <script> for(i=1;i<=10000;i++) { document.write(i + "<br>") ...