Spark Block Manager管理

Spark Block Manager管理

spark中的RDD-Cache, Shuffle-output, 以及broadcast的实现都是基于BlockManager来实现, BlockManager提供了数据存储(内存/文件存储)接口.

这里的Block和HDFS中谈到的Block块是有本质区别:HDFS中是对大文件进行分Block进行存储,Block大小固定为512M等;而Spark中的Block是用户的操作单位, 一个Block对应一块有组织的内存,一个完整的文件或文件的区间端,并没有固定每个Block大小的做法;

##BlockID和ManagerBuffer 上面谈到,Block是用户的操作单位,而这个操作对应的key就是这里BlockID,该Key所对应的真实数据内容为ManagerBuffer;
先提前看一下BlockDataManager这个数据接口,它在NetWork包中,对外提供了Block的操作.

trait BlockDataManager {
  def getBlockData(blockId: String): Option[ManagedBuffer]
  def putBlockData(blockId: String, data: ManagedBuffer, level: StorageLevel): Unit
}

我们看到BlockDataManager中有getBlockData和putBlockData两个接口,分别通过blockId获取一个ManagedBuffer,以及将一个blockId与ManagedBuffer对添加到 BlockManager中管理; 在Spark中BlockDataManager的唯一实现也就是我们这里谈到的BlockManager"服务".

BlockID本质上是一个字符串,但是在Spark中将它保证为"一组"case类,这些类的不同本质是BlockID这个命名字符串的不同,从而可以通过BlockID这个字符串来区别BlockID.

首先我们来看在Spark中Block类型,其实也就是开头谈到的RDD-Cache, Shuffle-output, 以及broadcast等;

  • RDDBlock:“rdd_” + rddId + “_” + splitIndex; 即每个RDD
    block表示一个特定rdd的一个分片
  • ShuffleBlock:多说一句关于shuffle,在Spark的1.1版本中发布一个sort版本的shuffle,原先的版本为hash,因此两种类型的shuffle也对应了两种数据结构
    • Hash版本,ShuffleBlock:“shuffle_” + shuffleId + “” + mapId + “” + reduceId
    • Sort版本,对于每一个bucket(shuffleId + “” + mapId + “” + reduceId组合)由ShuffleDataBlock和ShuffleIndexBlock两种block组成
      • “shuffle_” + shuffleId + “” + mapId + “” + reduceId + “.data”
      • “shuffle_” + shuffleId + “” + mapId + “” + reduceId + “.index”
  • BroadcastBlock:“broadcast_” + broadcastId + “_” + field)具体这里不多说,不感兴趣
  • TaskResultBlock:“taskresult_” +
    taskId;Spark中task运行的结果也是通过BlockManager进行管理
  • StreamBlock: “input-” + streamId + “-” +
    uniqueId应该是用于streaming中,不是特别感兴趣
  • TempBlock: “temp_” + id

通过上面的命名规则,我们可以快速确定每个Block的类型,以及相应的业务信息. 其中RDDBlock, ShuffleBlock, TaskResultBlock是个人比较感兴趣的三种Block.

再来看看ManagedBuffer, 本质上ManagedBuffer是一个对外Buffer的封装,这个类型在BlockManager内部使用并不多,外部通过BlockDataManager的接口来获取和 保存相应的Buffer到BlockManager中,这里我们首先简单的分析一下

ManagedBuffer.
sealed abstract class ManagedBuffer {
  def size: Long
  def nioByteBuffer(): ByteBuffer
  def inputStream(): InputStream
}

每个ManagedBuffer都有一个Size方法获取Buffer的大小,然后通过nioByteBuffer和inputStream两个接口对外提供了对Buffer的访问接口.至于这个Buffer具体存储方式由 子类来实现.比如ManagedBuffer的FileSegmentManagedBuffer子类实现了,将文件部分段转化为一个ManagedBuffer

final class FileSegmentManagedBuffer(val file: File, val offset: Long, val length: Long)
  extends ManagedBuffer {
  override def nioByteBuffer(): ByteBuffer = {
      var channel: FileChannel = null
        channel = new RandomAccessFile(file, "r").getChannel
        channel.map(MapMode.READ_ONLY, offset, length)
  }          
}

FileSegmentManagedBuffer通过NIO接口将文件Map到内存中,并返回ByteBuffer;注意这个nioByteBuffer函数是每次调用将会返回一个新的ByteBuffer,对它的操作不影响 真实的Buffer的offset和long

除了FileSegmentManagedBuffer实现以外, 还有NioByteBufferManagedBuffer(将一块已有的ByteBuffer内存封装为ManagedBuffer)和NettyByteBufManagedBuffer( 将netty中的ByteBuf内存封装为ManagedBuffer)

上面说到了ManagedBuffer只是BlockManager对外提供的Buffer表示,现在问题来了,这里谈到的BlockID对于的"Block块"在BlockManager服务中是怎么维护它的状态的呢?

BlockInfo和StorageLevel

上面谈到的Block在BlocManager中是怎么样的维护它的状态的呢?注意我们这里不去分析Block具体是怎么存储,后面会去分析;这里分析Block的属性信息;BlockManager 为了每个Block的属性信息来跟踪每个Block的状态.

首先来看StorageLevel, 在Spark中,对应RDD的cache有很多level选择,这里谈到的StorageLevel就是这块内容;首先我们来看存储的级别:

  • DISK,即文件存储
  • Memory,内存存储,这里的内存指的是Jvm中的堆内存,即onHeap
  • OffHeap, 非JVM中Heap的内存存储

对于DISK和Memory两种级别是可以同时出现的,而OffHeap与其他两个是互斥的.

关于OffHeap这里多说两句:JVM中如果直接进行内存分配都是受JVM来管理,使用的是JVM中内存堆,但是现在有很多技术可以在JVM代码中访问不受JVM管理的内存,即OffHeap内存; OffHeap最大的好处就是将内存的管理工作从JVM的GC管理器剥离出来由自己进行管理,特别是大对象,自定义生命周期的对象来说OffHeap很实用,可以减少GC的代销.

Spark中实现的OffHeap是基于Tachyon:分布式内存文件系统来实现的,在我们这篇分析文档中不会具体分析Tachyon的实现,有时间再去研究一下.

继续回到StorageLevel的分析; 除了三种存储级别以外,StorageLevel还

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值