Spark存储分析 - RDD存储调用与数据读写过程

通过之前章节,我们了解到RDD包含多个partition,每个Partition对应一个数据块Block,那么每个RDD中包含一个或多个数据块Block,每个Block拥有唯一的BlockId,对应数据块编号规则为:"rdd_" + rddId + "_" + splitIndex,其中splitIndex为该数据块对应Partition的序列号。

RDD存储调用

在存储级别一章中,我们知道在persist方法中并没有发生数据存储操作动作,实际发生数据操作是在任务运行过程中,RDD调用iterator方法时发生的。

  final def iterator(split: Partition, context: TaskContext): Iterator[T] = {
    if (storageLevel != StorageLevel.NONE) {
      // 如果存在存储级别,尝试读取内存的数据进行迭代计算
      SparkEnv.get.cacheManager.getOrCompute(this, split, context, storageLevel)
    } else {
      // 如果不存在存储级别,则直接读取数据进行迭代计算或读取检查点结果进行迭代计算
      computeOrReadCheckpoint(split, context)
    }
  }

getOrCompute方法是存储逻辑的核心,代码如下:

def getOrCompute[T](
  rdd: RDD[T],
  partition: Partition,
  context: TaskContext,
  storageLevel: StorageLevel): Iterator[T] = {

// 通过RDD的编号和Partition序号获取数据块Block的编号
val key = RDDBlockId(rdd.id, partition.index)
logDebug(s"Looking for partition $key")
// 通过BlockMananger获取Block数据
blockManager.get(key) match {
  // 该结果表示获取数据成功,并记录结果度量信息
  case Some(blockResult) =>
	// Partition is already materialized, so just return its values
	val existingMetrics = context.taskMetrics
	  .getInputMetricsForReadMethod(blockResult.readMethod)
	existingMetrics.incBytesRead(blockResult.bytes)

	val iter = blockResult.data.asInstanceOf[Iterator[T]]
	new InterruptibleIterator[T](context, iter) {
	  override def next(): T = {
		existingMetrics.incRecordsRead(1)
		delegate.next()
	  }
	}
  // 如果数据块不存在,则尝试读取检查点结果进行迭代计算
  case None =>
	// Acquire a lock for loading this partition
	// If another thread already holds the lock, wait for it to finish return its results
	val storedValues = acquireLockForPartition[T](key)
	if (storedValues.isDefined) {
	  return new InterruptibleIterator[T](context, storedValues.get)
	}

	// Otherwise, we have to load the partition ourselves
	try {
	  logInfo(s"Partition $key not found, computing it")
	  val computedValues = rdd.computeOrReadCheckpoint(partition, context)

	  // If the task is running locally, do not persist the result
	  if (context.isRunningLocally) {
		return computedValues
	  }

	  // Otherwise, cache the values and keep track of any updates in block statuses
	  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)
		loading.notifyAll()
	  }
	}
}
}


读数据过程

BlockManager的get方法是读数据的入口点,在读取时分为本地读取和远程节点读取两个步骤。本地读取使用getLocal方法,在该方法中根据不同的存储级别直接调用不同存储实现的方法;而远程节点读取使用getRemote方法,调用远程数据传输服务类BlockTransferService的fetchBlockSync进行处理,使用Netty的fetchBlocks方法获取数据。整个数据读取类调用如下图所示:

后续补上


写数据过程

BlockManager的putIterator方法是写数据的入口点。整个数据写入类调用关系如下图所示:

后续补上



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值