概要
Storage模块使用BlockStore存储数据,BlockStore是抽象类,其实现类之一MemoryStore负责内存数据的存储。
存储
BlockStore只定义了操作数据的抽象方法,MemoryStore继承BlockStore实现了相关方法,类图如下
内存中的数据使用MemoryEntry对象的value属性存储,并加了数据大小以及是否序列化的描述信息,MemoryEntry定义如下
(注:scala中Any类型等同于Java中的Object)
MemoryStore使用LinkedHashMap维护存储数据的MemoryEntry对象,如下
关于LinkedHashMap的使用补充以下两点
- LinkedHashMap内存使用双向链表维护数据的顺序(访问顺序或插入顺序),第三个参数为true时维护访问顺序,每次访问的数据被移至双向链表首位。
- LinkedHashMap的value为MemoryEntry对象,Key为BlockId,BlockId有三个主要实现类,RDDBlockId、ShuffleBlockId、BroadcastBlockId,分别存储RDD、Shuffle中间结果和Broadcast。
Serialize & Deserialize
内存中存储的数据有两种格式,即Serialize或Deserialize,通过StorageLevel对象设置,例如MEMORY_ONLY、MEMORY_ONLY_SER,后者表示内存中存储的是Serialize数据。
MemoryStore使用MemoryEntry对象的value属性存储数据,value的类型为Any,这是抽象类型,其实际存储类型和Serialize、Deserialize的对应关系如下
StorageLevel | Type | 解释 |
Serialize | java.nio.ByteBuffer | 例如StorageLevel为MEMORY_ONLY_SER时,原始数据若为Java对象(即Deserialize格式),会先被序列化为byte[],然后封装为ByteBuffer,存储到MemoryEntry对象的value属性中 |
Deserialize | Array[Any] | 例如StorageLevel为MEMORY_ONLY时,若原始数据为byte[],先反序列化为Array[Any],使用MemoryEntry对象的value属性存储,反序列化过程涉及到unroll及内存是否足够问题 |
Method
putXXX
BlockStore定义了三个存储数据的方法,putBytes、putArray、putIterator,从方法名可以判断出其输入的数据源格式不同,更多实现细节总结如下
putBytes | putArray | putIterator | |
输入数据格式 | java.nio.ByteBuffer(Serialize) | Array[Any](Deserialize) | Iterator[Any] |
Serialize(用户设置的StorageLevel) | 调用tryToPut | Serializer的serializeStream方法序列化Array[Any]并封装为ByteBuffer,调用tryToPut | 调用unrollSafely方法,尝试将数据安全加载到内存(避免OOM),若内存充足则加载到内存成功,unrollSafely返回Array[Any],接着调用putArray方法;若内存不足,unroll失败,交给DiskStore处理(磁盘存储) |
Deserialize(用户设置的StorageLevel) | Serializer的deserializeStream方法将ByteBuffer转换为Iterator[Any],调用putIterator | 调用tryToPut |
从上表可以看出,当putXXX方法的输入数据格式(Serialize或Deserialize)与StorageLevel设置相同时,调用tryToPut方法,否则会进行格式的转换,再调用相应方法。
tryToPut
putBytes、putArray、putIterator方法底层调用tryToPut方法,其主要逻辑如下
- 向MemoryManager申请所需要内存,如果内存不足,会尝试驱逐内存中现有数据以获得更多内存。
- 如果申请到足够的内存,数据存储MemoryEntry对象中,交给LinkedHashMap管理。
- 内存不足,清除数据,相关信息封装为droppedBlocks返回给调用者。
unrollSafely
putIterator方法处理的数据为Iterator[Any],此时不能直接在内存中处理数据,可能导致OOM,unrollSafely处理的方式是,每处理16个对象check内存使用情况,若内存足够,最终返回Array[Any],交给putArray方法处理,否则,将Iterator[Any]封装为PutResult,交由调用者处理。
总结
介绍MemoryStore的实现及几个重要方法。