Flink作为流计算代表性的框架,近几年来的热度越来越高,而Flink流处理,核心思想是有状态的流计算,这就要求在数据存储和管理要能跟得上场景需求,确保高性能,高可靠。今天的大数据开发学习分享,我们来讲讲,Flink内存数据结构。
Flink的内存管理像OS管理内存,划分为段和页。
1、内存段
内存段,即MemorySegment,是Flink内存抽象的最小分配单元。其实就是一个内存块,默认32KB。
MemorySegment可以在堆上:Java byte数组;也可以在堆外:基于Netty的ByteBuffer。
对于Java基本类型,MemorySegment可以直接读写二进制数据,对于其他类型,读取byte[]后反序列化,修改后序列化到MemorySegment。
HeapMemorySegment分配堆上内存,HybridMemorySegment分配堆外内存,实际上后来Flink用HybridMemorySegment分配堆外堆内内存。这设计JIT的编译优化。如果同时使用两个类,运行时每一次都要去查询函数表,确定调用哪个子类中的方法,无法提前优化。如果只是用一个实现子类,自动识别方法的调用都可以被虚化和内联,性能差在2.7倍左右。HybridMemorySegment使用Unsafe提供的一系列方法同时操作堆上和堆外内存。
2、内存页
MemorySegment的抽象粒度面向的是OS的内存管理,这种抽象对于上层的读写显然过于繁琐,Flink又抽象了一层,即内存页。内存页是MemorySegment之上的数据访问视图,数据读取抽象为DataInputView,数据写抽象为DataOutputView。
对于内存的读写是非常底层的行为,对于上层应用(DataStream作业)而言,涉及向MemorySegment写入、读取二进制的地方都使用到了DataInputView、DataOutputView,而不是直接使用MemorySegment。
3、Buffer
Task算子处理完数据后,将结果交给下游的时候,使用的抽象或者说内存对象是Buffer。其实现类是NetworkBuffer。一个NetworkBuffer包装了一个MemorySegment。
NetworkBuffer底层是MemorySegment。Buffer的申请释放由Flink自行管理,Flink引入了引用计数的概念,当有新的Buffer消费者,引用加一,当消费完,引用减1,最终当引用数变为0,就可以将Buffer释放了。
AbstractReferenceCountedByteBuf是Netty中的抽象类。通过实现该类,Flink拥有了引用计数控制Netty申请到的Buffer的内存的能力。
4、Buffer资源池
Buffer资源池:BufferPool,用来管理Buffer,包含Buffer的申请、释放、销毁、可用Buffer的通知。实现类是LocalBufferPool,每个Task拥有自己的LocalBufferPool。
为了方便对BufferPool的管理,设计了BufferPoolFactory,唯一实现类是NetworkBufferPool。每个TaskManager只有一个NetworkBufferPool。同一个TaskManager共享NetworkBufferPool。
关于大数据开发学习,Flink内存数据结构,以上就为大家做了大致的介绍了。Flink内存数据结构,建议要多深入,可以结合到源码去理解。
网友评论