赞
踩
基于JVM的数据分析引擎都需要面对将大量数据存到内存中,不得不面对JVM存在的几个问题
MemorySegment ≈ Flink 定制的 java.nio.ByteBuffer。
它的底层可以是一个普通的 Java 字节数组(byte[]),也可以是一个申请在堆外的 ByteBuffer。
每条记录都会以序列化的形式存储在一个或多个MemorySegment中。
1、Network Buffers: 一定数量的32KB大小的 buffer,主要用于数据的网络传输。
在 TaskManager 启动的时候就会分配。默认数量是 2048 个,
可以通过 taskmanager.network.numberOfBuffers 来配置。
2、Memory Manager Pool: 这是一个由 MemoryManager 管理的,由众多MemorySegment组成的超大集合
Flink 中的算法(如 sort/shuffle/join)会向这个内存池申请 MemorySegment,
将序列化后的数据存于其中,使用完后释放回内存池。
默认情况下,池子占了堆内存的 70% 的大小。
注意:只能用于批计算,不能用于流计算,所以在流式计算代码会占用此部分内存
3、Remaining (Free) Heap: 这部分的内存是留给用户代码以及 TaskManager 的数据结构使用的
这些数据结构一般都很小,所以基本上这些内存都是给用户代码使用的。
从GC的角度来看,可以把这里看成的新生代,也就是说这里主要都是由用户代码生成的短期对象。
Flink处理的数据超出了内存限制,则会将部分数据存储到硬盘上,下图展示了Flink处理数据时的存储过程
1、减少GC压力,
有常驻型数据都以二进制的形式存在 Flink 的MemoryManager中,不会被回收,
其他的数据对象是由用户代码生成的短生命周期对象,可以被GC快速回收,从而有效降低了垃圾回收的压力。
2、避免了OOM
所有运行时数据结构与算法只能向内存池申请,其内存大小是固定的,不会运行时因数据结构/算法发生OOM
在内存吃紧的情况下算法会高效的将内存块写到磁盘,之后在处理的时候读取回来。
3、节省内存空间
对象只存实际数据的二进制内容,可以避免这部分消耗
4、高效的二进制操作 & 缓存友好的计算
二进制数据以定义好的格式存储,可以高效地比较与操作;
该二进制形式可以把相关的值,以及hash值,键值和指针等相邻地放进内存中,使数据结构更为友好。
1、以直接通过偏移量,只是反序列化特定的对象成员变量。
2、如果对象的成员变量较多时,能够大大减少Java对象的创建开销,以及内存数据的拷贝大小。
针对前六种类型数据集,Flink皆可以自动生成对应的TypeSerializer,能非常高效地对数据集进行序列化和反序列化。
对于最后一种数据类型,Flink会使用Kryo进行序列化和反序列化。
当一个对象要加到 sort buffer 中时,
它的二进制数据会被加到第一个区域,
指针(可能还有key)会被加到第二个区域。
排序操作相关
关键操作:比大小&交换
1、会先用 key 比大小,这样就可以直接用二进制的key比较而不需要反序列化出整个对象;
2、key相同(或者没有提供二进制key),那就必须将真实的二进制数据反序列化出来,然后再做比较。
3、访问排序后的数据,可以沿着排好序的key+pointer区域顺序访问,
通过pointer找到对应的真实数据,并写到内存或外部
待续
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。