【Spark】Spark 存储原理--读数据过程

本篇结构:

  • 读取数据块过程
  • 内存读取
  • 磁盘读取
  • 远程读取

一、读取数据块过程

BlockManager 的 get 方法是读数据的入口点,有本地读取和远程读取两个分叉口。本地读取使用 getLocalValues 方法,根据存储级别的不同,使用 MemoryStore.getValues 或者 DiskStore.getBytes 读取数据。

远程读取使用 getRemoteValues 方法,调用远程数据传输服务类 BlockTransferService 的 fetchBlockSync 获取数据。

完整的数据读取过程如下:

二、内存读取

根据缓存的数据是否反序列化,getLocalValues 读取内存中的数据方法不同,如果反序列化,则调用 MemoryStore 的 getValues 方法,如果没有反序列化,则调用 MemoryStore 的 getBytes 方法。

BlockManager # getLocalValues:

if (level.useMemory && memoryStore.contains(blockId)) {
  // 如果反序列化,则直接读取内存中的数据
  val iter: Iterator[Any] = if (level.deserialized) {
    memoryStore.getValues(blockId).get
  } else {
   // 否则读取字节数组,并需要做反序列化处理
    serializerManager.dataDeserializeStream(
      blockId, memoryStore.getBytes(blockId).get.toInputStream())(info.classTag)
  }
  // We need to capture the current taskId in case the iterator completion is triggered
  // from a different thread which does not have TaskContext set; see SPARK-18406 for
  // discussion.
  // 返回数据及数据块大小、读取方法等
  val ci = CompletionIterator[Any, Iterator[Any]](iter, {
    releaseLock(blockId, taskAttemptId)
  })
  Some(new BlockResult(ci, DataReadMethod.Memory, info.size))

在 MemoryStore 中, getValues 和 getBytes 都根据 BlockId 获取内存中的数据块。

MemoryStore # getValues:

def getValues(blockId: BlockId): Option[Iterator[_]] = {
  val entry = entries.synchronized { entries.get(blockId) }
  entry match {
    case null => None
    case e: SerializedMemoryEntry[_] =>
      throw new IllegalArgumentException("should only call getValues on deserialized blocks")
    case DeserializedMemoryEntry(values, _, _) =>
      val x = Some(values)
      x.map(_.iterator)
  }
}

MemoryStore # getBytes:

def getBytes(blockId: BlockId): Option[ChunkedByteBuffer] = {
  val entry = entries.synchronized { entries.get(blockId) }
  entry match {
    case null => None
    case e: DeserializedMemoryEntry[_] =>
      throw new IllegalArgumentException("should only call getBytes on serialized blocks")
    case SerializedMemoryEntry(bytes, _, _) => Some(bytes)
  }
}

观察 entries,发现其实就是一个 LinkedHashMap。所以缓存在内存里的数据都是放入 LinkedHashMap 中。

private val entries = new LinkedHashMap[BlockId, MemoryEntry[_]](32, 0.75f, true)

LinkedHashMap 保存了插入的顺序,遍历 LinkedHashMap 时,先得到的记录是先插入的。如果内存不够,先保存的数据会被先清除。

三、磁盘读取

getLocalValues 方法中,根据缓存级别,如果使用磁盘缓存,则调用 DiskStore 的 getBytes 方法。

BlockManager # getLocalValues:

else if (level.useDisk && diskStore.contains(blockId)) {
    // 从磁盘中获取数据,由于保存到磁盘的数据是序列化的,读取到的数据也是序列化后的
    val diskData = diskStore.getBytes(blockId)
  val iterToReturn: Iterator[Any] = {
    if (level.deserialized) {
      // 如果储存级别需要反序列化,则先反序列化,然后根据是否 level.useMemory 的值,判断是否存储到内存中
      val diskValues = serializerManager.dataDeserializeStream(
        blockId,
        diskData.toInputStream())(info.classTag)
      maybeCacheDisk
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值