本篇结构:
- 读取数据块过程
- 内存读取
- 磁盘读取
- 远程读取
一、读取数据块过程
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