Spark内存存储MemoryStore & 磁盘存储DiskStore

简介

BlockManager中block的实际存储是由MemoryStore和DiskStore来实现的,根据Block要求的存储级别StorageLevel来决定是存储在内存还是磁盘,以及是序列化存储还是非序列化存储。MemoryStore和DiskStore是在BlockManager中初始化的。

// BlockManager.scala
// Actual storage of where blocks are kept
private[spark] val memoryStore =
  new MemoryStore(conf, blockInfoManager, serializerManager, memoryManager, this)
private[spark] val diskStore = new DiskStore(conf, diskBlockManager, securityManager)
memoryManager.setMemoryStore(memoryStore)

内存存储MemoryStore

MemoryStore负责将没有序列化的Java对象数组或者序列化的ByteBuffer存储到内存中。如果block的存储同时支持内存和磁盘存储,则优先存储到MemoryStore中,内存不够时再存储到DiskStore。

内存主要由堆内内存和堆外内存构成,其中堆外内存只能存储序列化后的值。MemoryStore中主要的成员是entries(即LinkedHashMap[BlockId, MemoryEntry]),用来保存块存储的映射。所有块的添加、获取和删除等操作,都是基于该映射结构实现的,因此对其所有的操作都需要加同步语义。

// Note: all changes to memory allocations, notably putting blocks, evicting blocks, and
// acquiring or releasing unroll memory, must be synchronized on `memoryManager`!

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

// A mapping from taskAttemptId to amount of memory used for unrolling a block (in bytes)
// All accesses of this map are assumed to have manually synchronized on `memoryManager`
private val onHeapUnrollMemoryMap = mutable.HashMap[Long, Long]()
// Note: off-heap unroll memory is only used in putIteratorAsBytes() because off-heap caching
// always stores serialized values.
private val offHeapUnrollMemoryMap = mutable.HashMap[Long, Long]()

// Initial memory to request before unrolling any block
private val unrollMemoryThreshold: Long =
  conf.getLong("spark.storage.unrollMemoryThreshold", 1024 * 1024)

数据存储方法putBytes

首先检查entries中是否已经存储了该blockId,如果已存储了则抛出IllegalArgumentException异常。然后检测MemoryStore中是否有足够的内存,没有则返回false,有则创建ByteBuffer并将其存储到entries中。

数据存储方法putIteratorAsValues & putIteratorAsBytes

putIteratorAsValues用于存储非序列化的block,而putIteratorAsBytes则是将block对象序列化后存储。

首先检查entries中是否已经存储了该blockId,如果已存储了则抛出IllegalArgumentException异常。由于迭代器可能太大而无法实现并存储在内存中。为避免OOM异常,方法将逐步展开迭代器,同时定期检查是否有足够的可用内存。如果成功物化了该block块,则在实体化期间使用的临时展开内存将“转移”到存储内存中,因此我们获得的内存不会超过存储该块所需的内存。如果成功,则返回存储的数据的估计大小。如果失败,则返回包含该块值的迭代器。返回的迭代器将由部分展开的块和原始输入迭代器的其余元素的组合。调用者必须完全使用此迭代器或对其调用close(),以释放部分展开的块占用的存储内存。

获取内存数据方法getBytes & getValues

getBytes和getValues方法都用于从entries中获取MemoryEntry。但是getBytes方法只可以获取序列化的block,getValues方法只可以获取非序列化的block。

磁盘存储DiskStore

将块存储到磁盘。blockSizes是保存块存储的主要结构,每个块以文件的方式存储到配置好的公共目录中,而该结构则保存已存储到磁盘上的块的大小信息。minMemoryMapBytes是表示如果块小于该值,则直接一次性读取到内存中,反之则以ChunkedByteBuffer的方式缓冲读取。

private val minMemoryMapBytes = conf.getSizeAsBytes("spark.storage.memoryMapThreshold", "2m")
private val blockSizes = new ConcurrentHashMap[String, Long]()

NIO读取方法getBytes

getBytes方法通过DiskBlockManager的getFile方法获取文件。然后使用NIO将文件读取到ByteBuffer中,并以BlockData格式返回。

NIO写入方法putBytes & put

putBytes方法实际是调用put方法实现的。put方法通过DiskBlockManager的getFile方法获取文件,然后将数据写入文件中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章