简单分析一下,理解其基本设计思想,基于Flink1.10
一、设计思想
1.1 数据DAG图

写数据的类是ResultPartition,读数据的类是InputChannel
1.2 内存分配图
看内存分配机制就好,其它不用管
TM之间:

TM内部:

1.3 NetworkBufferPool
NetworkBufferPool是TM粒度的,一个TM只有一个,不是任务粒度,读数据与写入数据的使用内存,都从其中进行申请,具体流程见上面的图
二、写数据时的内存分配源码分析
可以先看下ResultPartition类的属性

2.1 BufferPool
其中BufferPool的实现类是LocalBufferPool,它是一个内存池,是ResultPartition维度的,即每个ResultPartition都会有一个,创建时从NetworkBufferPool中申请获取内存,其属性如下:

-
其中currentPoolSize代表该LocalBufferPool的最大容量,即MemorySegment个数,
MemorySegment
的默认大小是 32 K,支持堆内和堆外内存 -
当LocalBufferPool需要获取MemorySegment时,会从availableMemorySegments中获取,如果availableMemorySegments中没有,且numberOfRequestedMemorySegments(已申请的segment数)< currentPoolSize,则去NetworkBufferPool中申请,否则返回null,然后阻塞生产者线程
-
numberOfRequiredMemorySegments和maxNumberOfMemorySegments由TM参数配置
-
NetworkBufferPool会根据LocalBufferPool的运行情况,根据其numberOfRequiredMemorySegments和maxNumberOfMemorySegments,动态调整LocalBufferPool的currentPoolSize
-
具体的数据写入类是ResultSubpartition,当ResultSubpartition需要写入数据时会从ResultPartition中的LocalBufferPool申请新的内存块,并放入自己的buffers中
三、读数据时的内存分配源码分析
读数据如果是本地内存读取,是直接通过方法调用的,把内存块传过去
如果是不同机器间的内存读取,需要从NetworkBufferPool中分配内存
RemoteInputChannel主要负责读取网络间的数据,从网络中收到的数据buffer,会拷贝在bufferQueue中的内存块中,并放在receivedBuffers中

bufferQueue代表的是可用内存,主要用于把通过网络传输收到的数据buffer的内容拷贝其中
bufferQueue包括独占内存和浮动内存,RemoteInputChannel初始化时,独占内存会从NetworkBufferPool中申请,数量为numberOfSegmentsToRequest,默认为2
浮动内存是当bufferQueue中内存不够用时,小于 初始可用内存+生产者没法送的内存,就会从InputGate中去申请

InputGate会从自己的bufferPool中去拿,bufferPool如果不够,会去NetworkBufferPool中申请
四、为什么要使用内存池
直接采用纳米ai的回答吧:
假设LocalBufferPool 是一个内存池,存储了100个MemorySegment,MemorySegment代表32kb内存,LocalBufferPool 用于数据传输
使用了内存池:
假设任务A需要将一批数据通过网络发送给任务B,流程如下:
- 申请内存段 :任务A从
LocalBufferPool
申请5个MemorySegment
(共160KB),用于暂存待发送的数据。 - 数据填充 :将待传输的数据序列化后,按顺序写入这5个
MemorySegment
中。 - 数据传输 :将填充好的
MemorySegment
通过网络层直接传输给任务B,避免数据拷贝(如Flink的NetworkBuffer机制1)。 - 接收与处理 :任务B从自己的
LocalBufferPool
中获取空闲的MemorySegment
接收数据,反序列化后处理。 - 释放内存 :任务A和任务B在处理完成后,将
MemorySegment
归还到各自的LocalBufferPool
中,供后续请求复用。
不使用内存池: