Spark源码分析之BlockStore

BlockStore是存储block抽象类,子类包括DiskStore,MemoryStore以及ExternalBlockStore等

 

一 DiskStore 磁盘存储

存储数据块(block)到磁盘,我我们可以在DiskStore中配置多个存放block的目录,DiskBlockManager会根据 这些配置创建不同的文件夹,存放block

 

二 MemoryStore 内存存储

# getSize 获取指定blockId对应的block文件大小

defgetSize(blockId:BlockId): Long = {
  diskManager.getFile(blockId.name).length
}

 

# put 放数据

def put(blockId: BlockId)(writeFunc: FileOutputStream => Unit): Unit = {
  // 判断指定的blockId对应的block的文件是否存在
  if (contains(blockId)) {
    throw new IllegalStateException(s"Block $blockId is already present in the disk store")
  }
  logDebug(s"Attempting to put block $blockId")
  val startTime = System.currentTimeMillis
  // 获取block所在文件
  val file = diskManager.getFile(blockId)
  // 构建文件输出流
  val fileOutputStream = new FileOutputStream(file)
  var threwException: Boolean = true
  try {
    // 将数据写入blockId指定的文件
    writeFunc(fileOutputStream)
    threwException = false
  } finally {
    try {
      Closeables.close(fileOutputStream, threwException)
    } finally {
       if (threwException) {
        remove(blockId)
      }
    }
  }
  val finishTime = System.currentTimeMillis
  logDebug("Block %s stored as %s file on disk in %d ms".format(
    file.getName,
    Utils.bytesToString(file.length()),
    finishTime - startTime))
}

 

# putBytes 据指定的byte数据,将其写入block文件

def putBytes(blockId: BlockId, bytes: ChunkedByteBuffer): Unit = {
  put(blockId) { fileOutputStream =>
    val channel = fileOutputStream.getChannel
    Utils.tryWithSafeFinally {
      bytes.writeFully(channel)
    } {
      channel.close()
    }
  }
}

 

二 MemoryStore

2.1 核心属性

BlockInfoManager blockInfoManager:跟踪单个数据块的元数据

SerializerManager serializerManager:序列化管理器

MemoryManager memoryManager:内存管理器

BlockEvictionHandler blockEvictionHandler:回收block的处理器

LinkedHashMap[BlockId, MemoryEntry[_]] entries:存放在内存的block数据

HashMap[Long, Long] onHeapUnrollMemoryMap:一个<taskAttemptId,一个已经展开的block已经使用了的内存量>的映射

HashMap[Long, Long] offHeapUnrollMemoryMap:开的block已经使用的内存(对外存储)

Long unrollMemoryThreshold:展开block之前初始化的内存阀值

2.2 重要的类和方法

# Long maxMemory :最大的内存大小

private def maxMemory: Long = {
  memoryManager.maxOnHeapStorageMemory + memoryManager.maxOffHeapStorageMemory
}

 

# memoryUsed 已经使用的内存,包括正展开的block使用的内存

private def memoryUsed: Long = memoryManager.storageMemoryUsed

 

# blocksMemoryUsed 经写完block占用的内存,不包括正在展开的block

private def blocksMemoryUsed: Long = memoryManager.synchronized {
  memoryUsed - currentUnrollMemory
}

 

# putBytes 往内存添加数据

往内存添加数据,如果内存足够,则创建ByteBuffer,然后放进MemoryStore,否则不会创建ByteBuffer

def putBytes[T: ClassTag](blockId: BlockId,
    size: Long, memoryMode: MemoryMode,
    _bytes: () => ChunkedByteBuffer): Boolean = {
  require(!contains(blockId), s"Block $blockId is already present in the MemoryStore")
  // 通过MemoryManager申请storage内存
  if (memoryManager.acquireStorageMemory(blockId, size, memoryMode)) {
   // 如果为这个block申请到了足够的内存
    val bytes = _bytes()
    assert(bytes.size == size)
    // 创建一个SerializedMemoryEntry,然后放入内存
    val entry = new SerializedMemoryEntry[T](bytes, memoryMode, implicitly[ClassTag[T]])
    entries.synchronized {
      entries.put(blockId, entry)
    }
    logInfo("Block %s stored as bytes in memory (estimated size %s, free %s)".format(
      blockId, Utils.bytesToString(size), Utils.bytesToString(maxMemory - blocksMemoryUsed)))
    true
  } else {
    false
  }
}

 

# putIteratorAsValues 尝试将一个迭代器放到block

private[storage] def putIteratorAsValues[T](
    blockId: BlockId,
    values: Iterator[T],
    classTag: ClassTag[T]): Either[PartiallyUnrolledIterator[T], Long] = {

  require(!contains(blockId), s"Block $blockId is already present in the MemoryStore")

  // 目前为止已经展开了多少元素
  var elementsUnrolled = 0
  // 是否这里还有足够的内存继续保持这个block展开
  var keepUnrolling = true
  // Initial per-task memory to request for unrolling blocks (bytes).
  // 对于展开的数据块,初始化配一个任务内存
  val initialMemoryThreshold = unrollMemoryThreshold
  // 多久检查一次我们是否需要请求更多的内存
  val memoryCheckPeriod = 16
  // 这个task预留的内存
  var memoryThreshold = initialMemoryThreshold
  // 内存请求增长因子
  val memoryGrowthFactor = 1.5
  // 跟踪特殊block或者putIterator操作展开的内存
  var unrollMemoryUsedByThisBlock = 0L
  // 对于展开的block构建一个vector,只用于添加,然后预计其大小
  var vector = new SizeTrackingVector[T]()(classTag)

  // 为任务预定内存用于展开指定的block,因为展开block也需要消费内存
  keepUnrolling =
    reserveUnrollMemoryForThisTask(blockId, initialMemoryThreshold, MemoryMode.ON_HEAP)
  // 如果没有预定的内存为展开指定的block,给出警告信息
  if (!keepUnrolling) {
    logWarning(s"Failed to reserve initial memory threshold of " +
      s"${Utils.bytesToString(initialMemoryThreshold)} for computing block $blockId in memory.")
  } else {
    // 否则这个unrollMemoryUsedByThisBlock 需要加上初始化内存阀值initialMemoryThreshold,表示预留给展开block的内存
    unrollMemoryUsedByThisBlock += initialMemoryThreshold
  }

  // 安全展开block,定期检查是否我们超过阀值
  while (values.hasNext && keepUnrolling) {
    // 迭代每一个vlaue
    vector += values.next()
    // 是否达到我们需要进行内存申请检测
    if (elementsUnrolled % memoryCheckPeriod == 0) {
      // 如果满足条件触发了检测,先获取预估的大小,如果预估的大小超过了内存阀值
      val currentSize = vector.estimateSize()
      // 如果超过了阀值,则需要申请更多的内存,申请算法(当前大小 * 内存增长因子 - 内存阀值)
      if (currentSize >= memoryThreshold) {
        val amountToRequest = (currentSize * memoryGrowthFactor - memoryThreshold).toLong
        // 再次为task申请预定用于展开block的内存
        keepUnrolling =
          reserveUnrollMemoryForThisTask(blockId, amountToRequest, MemoryMode.ON_HEAP)
        // 如果预订成功
        if (keepUnrolling) {
          // 则更新unrollMemoryUsedByThisBlock
          unrollMemoryUsedByThisBlock += amountToRequest
        }
        // 当前内存的阀值也需要更新了
        memoryThreshold += amountToRequest
      }
    }
    // 更新滚动元素
    elementsUnrolled += 1
  }
  // 如果预定成功
  if (keepUnrolling) {
    // vector转变位数组
    val arrayValues = vector.toArray
    vector = null
    // 创建一个反序列化的DeserializedMemoryEntry对象
    val entry =
      new DeserializedMemoryEntry[T](arrayValues, SizeEstimator.estimate(arrayValues), classTag)
    val size = entry.size
    def transferUnrollToStorage(amount: Long): Unit = {
      // Synchronize so that transfer is atomic
      memoryManager.synchronized {
        releaseUnrollMemoryForThisTask(MemoryMode.ON_HEAP, amount)
        val success = memoryManager.acquireStorageMemory(blockId, amount, MemoryMode.ON_HEAP)
        assert(success, "transferring unroll memory to storage memory failed")
      }
    }
    // Acquire storage memory if necessary to store this block in memory.
    // 如果需要在内存存储block,申请storage内存
    val enoughStorageMemory = {
      // 滚动这个block使用内存 < block的大小
      if (unrollMemoryUsedByThisBlock <= size) {
        // 我们需要申请额外的storage内存
        val acquiredExtra =
          memoryManager.acquireStorageMemory(
            blockId, size - unrollMemoryUsedByThisBlock, MemoryMode.ON_HEAP)
        if (acquiredExtra) {
          transferUnrollToStorage(unrollMemoryUsedByThisBlock)
        }
        acquiredExtra
      } else { // unrollMemoryUsedByThisBlock > size
        val excessUnrollMemory = unrollMemoryUsedByThisBlock - size
        releaseUnrollMemoryForThisTask(MemoryMode.ON_HEAP, excessUnrollMemory)
        transferUnrollToStorage(size)
        true
      }
    }
    // storage内存足够的话,entry放入内存中
    if (enoughStorageMemory) {
      entries.synchronized {
        entries.put(blockId, entry)
      }
      logInfo("Block %s stored as values in memory (estimated size %s, free %s)".format(
        blockId, Utils.bytesToString(size), Utils.bytesToString(maxMemory - blocksMemoryUsed)))
      Right(size)
    } else {
      assert(currentUnrollMemoryForThisTask >= unrollMemoryUsedByThisBlock,
        "released too much unroll memory")
      Left(new PartiallyUnrolledIterator(
        this,
        MemoryMode.ON_HEAP,
        unrollMemoryUsedByThisBlock,
        unrolled = arrayValues.toIterator,
        rest = Iterator.empty))
    }
  } else {
    // 没有足够展开内存用于打开block
    logUnrollFailureMessage(blockId, vector.estimateSize())
    Left(new PartiallyUnrolledIterator(
      this,
      MemoryMode.ON_HEAP,
      unrollMemoryUsedByThisBlock,
      unrolled = vector.iterator,
      rest = values))
  }
}

 

# remove 从内存中删除某一个blockId对应的数据

def remove(blockId: BlockId): Boolean = memoryManager.synchronized {
  // 从内存中删除
  val entry = entries.synchronized {
    entries.remove(blockId)
  }
  if (entry != null) {
    entry match {
      case SerializedMemoryEntry(buffer, _, _) => buffer.dispose()
      case _ =>
    }
    // 开始释放storage内存
    memoryManager.releaseStorageMemory(entry.size, entry.memoryMode)
    logDebug(s"Block $blockId of size ${entry.size} dropped " +
      s"from memory (free ${maxMemory - blocksMemoryUsed})")
    true
  } else {
    false
  }
}

 

# evictBlocksToFreeSpace 试图回收block已释放内存空间

private[spark] def evictBlocksToFreeSpace(
    blockId: Option[BlockId], space: Long,
    memoryMode: MemoryMode): Long = {
  assert(space > 0)

  memoryManager.synchronized {
    var freedMemory = 0L // 剩余内存
    val rddToAdd = blockId.flatMap(getRddId)
    // 选中的block
    val selectedBlocks = new ArrayBuffer[BlockId]
    // 判断block是否可以被回收
    def blockIsEvictable(blockId: BlockId, entry: MemoryEntry[_]): Boolean = {
      entry.memoryMode == memoryMode && (rddToAdd.isEmpty || rddToAdd != getRddId(blockId))
    }

    entries.synchronized {
      // 遍历每一个entry元素
      val iterator = entries.entrySet().iterator()
      // 剩余的内存小于block大小
      while (freedMemory < space && iterator.hasNext) {
        val pair = iterator.next()
        val blockId = pair.getKey
        val entry = pair.getValue
        if (blockIsEvictable(blockId, entry)) {
          // 更新被选中block
          if (blockInfoManager.lockForWriting(blockId, blocking = false).isDefined) {
            selectedBlocks += blockId
            freedMemory += pair.getValue.size
          }
        }
      }
    }
    // 删除block
    def dropBlock[T](blockId: BlockId, entry: MemoryEntry[T]): Unit = {
      val data = entry match {
        case DeserializedMemoryEntry(values, _, _) => Left(values)
        case SerializedMemoryEntry(buffer, _, _) => Right(buffer)
      }
      // 从内存中删除
      val newEffectiveStorageLevel =
        blockEvictionHandler.dropFromMemory(blockId, () => data)(entry.classTag)
      if (newEffectiveStorageLevel.isValid) {
        // The block is still present in at least one store, so release the lock
        // but don't delete the block info
        blockInfoManager.unlock(blockId)
      } else {
        // The block isn't present in any store, so delete the block info so that the
        // block can be stored again
        blockInfoManager.removeBlock(blockId)
      }
    }
    // 如果空闲内存大于block大小的时候
    if (freedMemory >= space) {
      logInfo(s"${selectedBlocks.size} blocks selected for dropping " +
        s"(${Utils.bytesToString(freedMemory)} bytes)")
      // 开始遍历那些可以从内存中移除的blockId,并且调用dropBlock进行移除
      for (blockId <- selectedBlocks) {
        val entry = entries.synchronized { entries.get(blockId) }
        // This should never be null as only one task should be dropping
        // blocks and removing entries. However the check is still here for
        // future safety.
        if (entry != null) {
          dropBlock(blockId, entry)
        }
      }
      logInfo(s"After dropping ${selectedBlocks.size} blocks, " +
        s"free memory is ${Utils.bytesToString(maxMemory - blocksMemoryUsed)}")
      freedMemory
    } else {
      blockId.foreach { id =>
        logInfo(s"Will not store $id")
      }
      selectedBlocks.foreach { id =>
        blockInfoManager.unlock(id)
      }
      0L
    }
  }
}

 

# reserveUnrollMemoryForThisTask 为任务预定内存用于展开指定的block,因为展开block也需要消费内存

def reserveUnrollMemoryForThisTask(
    blockId: BlockId,
    memory: Long,
    memoryMode: MemoryMode): Boolean = {
  memoryManager.synchronized {
    val success = memoryManager.acquireUnrollMemory(blockId, memory, memoryMode)
    if (success) {
      val taskAttemptId = currentTaskAttemptId()
      val unrollMemoryMap = memoryMode match {
        case MemoryMode.ON_HEAP => onHeapUnrollMemoryMap
        case MemoryMode.OFF_HEAP => offHeapUnrollMemoryMap
      }
      unrollMemoryMap(taskAttemptId) = unrollMemoryMap.getOrElse(taskAttemptId, 0L) + memory
    }
    success
  }
}

 

def releaseUnrollMemoryForThisTask(memoryMode: MemoryMode, memory: Long = Long.MaxValue): Unit = {
  val taskAttemptId = currentTaskAttemptId()
  memoryManager.synchronized {
    val unrollMemoryMap = memoryMode match {
      case MemoryMode.ON_HEAP => onHeapUnrollMemoryMap
      case MemoryMode.OFF_HEAP => offHeapUnrollMemoryMap
    }
    if (unrollMemoryMap.contains(taskAttemptId)) {
      val memoryToRelease = math.min(memory, unrollMemoryMap(taskAttemptId))
      if (memoryToRelease > 0) {
        unrollMemoryMap(taskAttemptId) -= memoryToRelease
        memoryManager.releaseUnrollMemory(memoryToRelease, memoryMode)
      }
      if (unrollMemoryMap(taskAttemptId) == 0) {
        unrollMemoryMap.remove(taskAttemptId)
      }
    }
  }
}

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

莫言静好、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值