RDD的缓存

RDD的缓存和RDD的checkpoint的区别

缓存是在计算结束后,直接将计算结果通过用户定义的存储级别(存储级别定义了缓存存储的介质,现在支持内存、本地文件系统和Tachyon)写入不同的介质。

而检查点不同,它是在计算完成后,重新建立一个Job来计算。

为了避免重复计算,推荐先将RDD缓存,这样就能保证检查点的操作可以快速完成。

  RDD的缓存能够在第一次计算完成后,将计算结果保存到内存、本地文件系统或者Tachyon(分布式内存文件系统)中。通过缓存,Spark避免了RDD上的重复计算,能够极大地提升计算速度。

  Spark速度非常快的原因之一,就是在不同操作中在内存中持久化(或缓存)一个数据集。当持久化一个RDD后,每一个节点都将把计算的分片结果保存在内存中,并在对此数据集(或者衍生出的数据集)进行的其他动作(action)中重用。这使得后续的动作变得更加迅速(通常快10倍)。RDD相关的持久化和缓存,是Spark最重要的特征之一。可以说,缓存是Spark构建迭代式算法和快速交互式查询的关键。

  通过persist()或cache()方法可以标记一个要被持久化的RDD,一旦首次被触发,该RDD将会被保留在计算节点的内存中并重用。实际上,cache()是使用persist()的快捷方法,它们的实现如下:

/** Persist this RDD with the default storage level (`MEMORY_ONLY`). */
def persist(): this.type = persist(StorageLevel.MEMORY_ONLY)
/** Persist this RDD with the default storage level (`MEMORY_ONLY`). */
def cache(): this.type = persist()

图1中,假设首先进行了RDD0→RDD1→RDD2的计算作业,那么计算结束时,RDD1就已经缓存在系统中了。在进行RDD0→RDD1→RDD3的计算作业时,由于RDD1已经缓存在系统中,因此RDD0→RDD1的转换不会重复进行,计算作业只须进行RDD1→RDD3的计算就可以了,因此计算速度可以得到很大提升。

              图1  RDD缓存过的Partition可以加快下一次的计算速度

缓存有可能丢失,或者存储于内存的数据由于内存不足而被删除。RDD的缓存的容错机制保证了即使缓存丢失也能保证计算的正确执行。通过基于RDD的一系列的转换,丢失的数据会被重算。RDD的各个Partition是相对独立的,因此只需要计算丢失的部分即可,并不需要重算全部Partition。

  RDD的缓存的处理

  如果存储级别不是NONE,那么先检查是否有缓存;没有缓存则要进行计算。什么是存储级别?从用户的角度来看就是缓存保存到不同的存储位置,比如内存、硬盘、Tachyon;还有缓存的数据是否需要序列化等。
  cacheManager对Storage模块进行了封装,使得RDD可以更加简单地从Storage模块读取或者写入数据。RDD的每个Partition对应Storage模块的一个Block,只不过Block是Partition经过处理后的数据。在系统实现的层面上,可以认为Partition和Block是一一对应的。cacheManager会通过getOrCompute来判断当前的RDD是否需要进行计算。

  首先,cacheManager会通过RDD的ID和当前计算的Partition的ID向Storage模块的BlockManager发起查询请求,如果能够获得Block的信息,会直接返回Block的信息。否则,代表该RDD是需要计算的。这个RDD以前可能计算过并且被存储到了内存中,但是后来由于内存紧张,这部分内存被清理了。在计算结束后,计算结果会根据用户定义的存储级别,写入BlockManager中。这样,下次就可以不经过计算而直接读取该RDD的计算结果了。

核心实现逻辑如下:

def getOrCompute[T](
    rdd: RDD[T],
    partition: Partition,

    context: TaskContext,
    storageLevel: StorageLevel): Iterator[T] = {
  //获取RDD的BlockId
  val key = RDDBlockId(rdd.id, partition.index)
  logDebug(s"Looking for partition $key")

  blockManager.get(key) match { //向BlockManager查询是否有缓存

case Some(blockResult) => //缓存命中
  //更新统计信息,将缓存作为结果返回
  context.taskMetrics.inputMetrics = Some(blockResult.inputMetrics)

  new InterruptibleIterator(context, blockResult.data.asInstanceOf[Iterator[T]])
case None => //没有缓存命中,需要计算
  //判断当前是否有线程在处理当前的Partition,如果有那么等待它结束后,直接从Block
  // Manager中读取处理结果如果没有线程在计算,那么storedValues就是None,否则

  //就是计算的结果
  val storedValues = acquireLockForPartition[T](key)
  if (storedValues.isDefined) { //已经被其他线程处理了,直接返回结果

    return new InterruptibleIterator[T](context, storedValues.get)
  }

//需要计算
try {
//如果被checkpoint过,那么读取checkpoint的数据;否则调用rdd的compute()开始计算

val computedValues = rdd.computeOrReadCheckpoint(partition,context)
// Task是在Driver端执行的话就不需要缓存结果,这个主要是为了first()或者take()
//这种仅仅有一个执行阶段的任务的快速执行。这类任务由于没有Shuffle阶段,直接运行

//在Driver端可能会更省时间

if (context.isRunningLocally) {
return computedValues

}

//将计算结果写入到BlockManager
val updatedBlocks = new ArrayBuffer[(BlockId, BlockStatus)]
val cachedValues =
  putInBlockManager(key, computedValues, storageLevel, updatedBlocks)

//更新任务的统计信息

val metrics = context.taskMetrics
val lastUpdatedBlocks = metrics.updatedBlocks.getOrElse(
  Seq[(BlockId, BlockStatus)]())
metrics.updatedBlocks = Some(lastUpdatedBlocks ++ updatedBlocks.toSeq)
new InterruptibleIterator(context, cachedValues)
} finally {
loading.synchronized {
loading.remove(key)

//如果有其他的线程在等待该Partition的处理结果,那么通知它们计算已经完成,结果已

//经存到BlockManager中(注意前面那类不会写入BlockManager的本地任务)

// loading.notifyAll()
      }
    }
  }
}

Spark RDD概念学习系列之RDD的缓存(八)的更多相关文章

  1. Spark RDD概念学习系列之RDD的checkpoint(九)

     RDD的检查点 首先,要清楚.为什么spark要引入检查点机制?引入RDD的检查点?  答:如果缓存丢失了,则需要重新计算.如果计算特别复杂或者计算耗时特别多,那么缓存丢失对于整个Job的影响是不容 ...

  2. Spark RDD概念学习系列之RDD是什么?(四)

       RDD是什么? 通俗地理解,RDD可以被抽象地理解为一个大的数组(Array),但是这个数组是分布在集群上的.详细见  Spark的数据存储 Spark的核心数据模型是RDD,但RDD是个抽象类 ...

  3. Spark RDD概念学习系列之RDD的转换(十)

    RDD的转换 Spark会根据用户提交的计算逻辑中的RDD的转换和动作来生成RDD之间的依赖关系,同时这个计算链也就生成了逻辑上的DAG.接下来以“Word Count”为例,详细描述这个DAG生成的 ...

  4. Spark RDD概念学习系列之RDD的操作(七)

    RDD的操作 RDD支持两种操作:转换和动作. 1)转换,即从现有的数据集创建一个新的数据集. 2)动作,即在数据集上进行计算后,返回一个值给Driver程序. 例如,map就是一种转换,它将数据集每 ...

  5. Spark RDD概念学习系列之RDD的依赖关系(宽依赖和窄依赖)(三)

    RDD的依赖关系?   RDD和它依赖的parent RDD(s)的关系有两种不同的类型,即窄依赖(narrow dependency)和宽依赖(wide dependency). 1)窄依赖指的是每 ...

  6. Spark RDD概念学习系列之RDD的缺点(二)

        RDD的缺点? RDD是Spark最基本也是最根本的数据抽象,它具备像MapReduce等数据流模型的容错性,并且允许开发人员在大型集群上执行基于内存的计算. 为了有效地实现容错,(详细见ht ...

  7. Spark RDD概念学习系列之rdd的依赖关系彻底解密(十九)

    本期内容: 1.RDD依赖关系的本质内幕 2.依赖关系下的数据流视图 3.经典的RDD依赖关系解析 4.RDD依赖关系源码内幕 1.RDD依赖关系的本质内幕 由于RDD是粗粒度的操作数据集,每个Tra ...

  8. Spark RDD概念学习系列之RDD与DSM的异同分析(十三)

    RDD是一种分布式的内存抽象,下表列出了RDD与分布式共享内存(Distributed Shared Memory,DSM)的对比. 在DSM系统[1]中,应用可以向全局地址空间的任意位置进行读写操作 ...

  9. Spark RDD概念学习系列之RDD的容错机制(十七)

    RDD的容错机制 RDD实现了基于Lineage的容错机制.RDD的转换关系,构成了compute chain,可以把这个compute chain认为是RDD之间演化的Lineage.在部分计算结果 ...

随机推荐

  1. 注意map<> 的[]

    其实在之前一篇关于map的基本操作中已经提到过注意[]操作,这里再强调一下. 先看下面的程序: #include<iostream> #include<map> using n ...

  2. Git基础(二)

    本章内容将介绍第二部分:本地仓储操作.为了加深对每个命令操作的理解,我们可以事先获取一个仓储边学边操作.这里首先 你要明白一个概念,所有本地工作目录下的文件状态无不在乎两种,已跟踪和未跟踪.已跟踪文件 ...

  3. HDU 1244 Max Sum Plus Plus Plus

    虽然这道题看起来和 HDU 1024  Max Sum Plus Plus 看起来很像,可是感觉这道题比1024要简单一些 前面WA了几次,因为我开始把dp[22][maxn]写成dp[maxn][2 ...

  4. UIImagePicker照片选择器

    UIImagePickerController 1.+(BOOL)isSourceTypeAvailable:(UIImagePickerControllerSourceType)sourceType ...

  5. C# list installed softwares

    ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_Product&q ...

  6. Using newInstance() to Instantiate a Fragment(转)

    I recently came across an interesting question on StackOverflow regarding Fragment instantiation: Wh ...

  7. python练习程序(c100经典例18)

    题目: 求s=a+aa+aaa+aaaa+aa...a的值,其中a是一个数字.例如2+22+222+2222+22222(此时共有5个数相加),几个数相加有键盘控制. def foo(a,n): su ...

  8. phonegap 单例模式

    今天在使用云推送的时候  app打开着,然后 控制台推送一条消息 结果点击后又重新打开了一个客户端... ok,我需要的是单例,我使用了singleInstance  ,达到了效果. 引用百度知道的 ...

  9. 基于HTTP的直播点播HLS

             HLS(HTTP Live Streaming) 是Apple在2009年发布的,可以通过普通的web服务器进行分发的新型流媒体协议.苹果官方对于视频直播服务提出了 HLS 解决方案 ...

  10. 支持 MBTiles 规范的预缓存

    SuperMap iServer 支持生成符合MBTiles规范的预缓存(MBTiles是由MapBox制定的一种将瓦片地图数据存储到SQLite数据库中并可快速使用,管理和分享的规范. 该规范由Ma ...