本篇结构:
- 写数据块过程
- 写内存
- 写磁盘
- 写远程
一、写数据块过程
分析读数据过程时,可以了解到 RDD.iterator -> RDD.getOrCompute -> BlockManager.getOrElseUpdate 既是读数据的入口,也是写数据的入口。
不同的是,读数据走 BlockManager.get 方法,而写数据走 doPutIterator 方法。
在 doPutIterator 方法中,如果缓存到内存中,则需要先判断数据是否进行了反序列化,如果已经反序列化,调用 putIteratorAsValues 直接把数据存入内存,读取时不需要再进行反序列化,如果没有反序列化,则调用 putIteratorAsBytes 方法将序列化数据缓存,读取时需要进行反序列化。在存入内存时,需要判断在内存中展开该数据大小是否足够,如果足够,MemoryStore 直接 存入 entries 中,如果不够,如果启用磁盘存储,则存入磁盘。
数据写入完成时,一方面把数据块的元数据发送给 Driver 端的 BlockManagerMasterEndpoint 终端点,更新元数据。另一方面判断是否需要创建数据副本,如果需要则调用 replicate 方法,把数据写到远程节点。
过程图如下:
BlockManager # doPutIterator:
private def doPutIterator[T](
blockId: BlockId,
iterator: () => Iterator[T],
level: StorageLevel,
classTag: ClassTag[T],
tellMaster: Boolean = true,
keepReadLock: Boolean = false): Option[PartiallyUnrolledIterator[T]] = {
doPut(blockId, level, classTag, tellMaster = tellMaster, keepReadLock = keepReadLock) { info =>
val startTimeMs = System.currentTimeMillis
var iteratorFromFailedMemoryStorePut: Option[PartiallyUnrolledIterator[T]] = None
// Size of the block in bytes
var size = 0L
// 缓存到内存中
if (level.useMemory) {
// Put it in memory first, even if it also has useDisk set to true;
// We will drop it to disk later if the memory store can't hold it.
if (level.deserialized) {
memoryStore.putIteratorAsValues(blockId, iterator(), classTag) match {
// 写入内存成功,返回数据块大小
case Right(s) =>
size = s
// 写入失败,运行存入磁盘则进行写磁盘操作,否则返回结果
case Left(iter) =>
// Not enough space to unroll this block; drop to disk if applicable
if (level.useDisk) {
logWarning(s"Persisting block $blockId to disk instead.")
diskStore.put(blockId) { channel =>
val out = Channels.newOutputStream(channel)
serializerManager.dataSerializeStream(blockId, out, iter)(classTag)
}
size = diskStore.getSize(blockId)
} else {
iteratorFromFailedMemoryStorePut = Some(iter)
}
}
} else { // !level.deserialized
memoryStore.putIteratorAsBytes(blockId, iterator(), classTag, level.memoryMode) match {
case Right(s) =>
size = s
case Left(partiallySerializedValues) =>
// Not enough space to unroll this block; drop to disk if applicable
if (level.useDisk) {
logWarning(s"Persisting block $blockI