flink的内存模型
内存占比:
Framework Heap:128M
Task Heap:剩余多少是多少 Managed Memory:占比 flink的0.4
Framework Off-Heap:128M
Task Off-Heap:0
Network:占比flink的0.1 阈值范围64M-1G
JVM Metaspace:256M JVM Overhead:占比0.1 阈值范围192M-1G
并行度的计算
开发完成后,先进行压测。任务并行度给 10 以下,测试单个并行度的处理上限。
总 QPS/单并行度的处理能力 = 并行度
开发完 Flink 作业,压测的方式很简单,先在 kafka 中积压数据,之后开启 Flink 任务, 出现反压,就是处理瓶颈。相当于水库先积水,一下子泄洪。
不能只从 QPS 去得出并行度,因为有些字段少、逻辑简单的任务,单并行度一秒处理 几万条数据。而有些数据字段多,处理逻辑复杂,单并行度一秒只能处理 1000 条数据。
最好根据高峰期的 QPS 压测,并行度*1.2 倍,富余一些资源。
Source并行度的配置
数据源端是 Kafka,Source 的并行度设置为 Kafka 对应 Topic 的分区数。如果已经等于 Kafka 的分区数,消费速度仍跟不上数据生产速度,考虑下Kafka 要扩大分区,同时调大并行度等于分区数。
Flink 的一个并行度可以处理一至多个分区的数据,如果并行度多于 Kafka 的分区数, 那么就会造成有的并行度空闲,浪费资源。
Transform端并行度的配置
Keyby 之前的算子
一般不会做太重的操作,都是比如 map、filter、flatmap 等处理较快的算子,并行度 可以和 source 保持一致。
Keyby 之后的算子
如果并发较大,建议设置并行度为 2 的整数次幂,例如:128、256、512; 小并发任务的并行度不一定需要设置成 2 的整数次幂; 大并发任务如果没有 KeyBy,并行度也无需设置为 2 的整数次幂;
Sink 端并行度的配置
Sink 端是数据流向下游的地方,可以根据 Sink 端的数据量及下游的服务抗压能力进 行评估。如果 Sink 端是 Kafka,可以设为 Kafka 对应 Topic 的分区数。
Sink 端的数据量小,比较常见的就是监控告警的场景,并行度可以设置的小一些。
Source 端的数据量是最小的,拿到 Source 端流过来的数据后做了细粒度的拆分,数据量不断的增加,到 Sink 端的数据量就非常大。那么在 Sink 到下游的存储中间件的时候 就需要提高并行度。
checkpoint的设置
checkpoint的设置需要考虑时效性的要求,需要在时效性和性能之间做一个平衡,如果时效性要求高,结合 end- to-end 时长,设置秒级或毫秒级。如果 Checkpoint 语义配置为 EXACTLY_ONCE,那么在 Checkpoint 过程中还会存在 barrier 对齐的过程, 可以通过 Flink Web UI 的 Checkpoint 选项卡来查看 Checkpoint 过程中各阶段的耗 时情况,从而确定到底是哪个阶段导致 Checkpoint 时间过长然后针对性的解决问题。
checkpoint一般需要设置两个参数:
(1)间隔: 分钟或者秒级 (根据需求定义)
(2)最小等待间隔:参考间隔 (建议一半)
其他参数:
超时时间:默认10分钟,参考间隔,结合end-to-end调整(目前我们时间为30s主要是考虑到kafka告警问题)
失败次数
保留ck
反压的处理
简单来说,Flink 拓扑中每个节点(Task)间的数据都以阻塞队列的方式传输,下游来 不及消费导致队列被占满后,上游的生产也会被阻塞,最终导致数据源的摄入被阻塞。
反压的危害
- 影响 checkpoint 时长:barrier 不会越过普通数据,数据处理被阻塞也会导致 checkpoint barrier 流经整个数据管道的时长变长,导致 checkpoint 总体时间(End to End Duration)变长。
- 影响 state 大小:barrier 对齐时,接受到较快的输入管道的 barrier 后,它后面数 据会被缓存起来但不处理,直到较慢的输入管道的 barrier 也到达,这些被缓存的数据会 被放到 state 里面,导致 checkpoint 变大。
这两个影响对于生产环境的作业来说是十分危险的,因为 checkpoint 是保证数据一 致性的关键,checkpoint 时间变长有可能导致 checkpoint 超时失败,而 state 大小同 样可能拖慢 checkpoint 甚至导致 OOM (使用 Heap-based StateBackend)或者物理 内存使用超出容器资源的稳定性问题。
定位反压问题
解决反压首先要做的是定位到造成反压的节点,排查的时候,先把 operator chain 禁 用,方便定位到具体算子。
- 通过Flink Web UI 的反压监控提供了 SubTask 级别的反压监控
- 分析瓶颈算子
如果处于反压状态,那么有两种可能性:
(1)该节点的发送速率跟不上它的产生数据速率。这一般会发生在一条输入多条输出的 Operator(比如 flatmap)。这种情况,该节点是反压的根源节点,它是从 Source Task 到 Sink Task 的第一个出现反压的节点。
(2)下游的节点接受速率较慢,通过反压机制限制了该节点的发送速率。这种情况, 需要继续排查下游节点,一直找到第一个为 OK 的一般就是根源节点。
- Metrics定位
监控反压时会用到的 Metrics 主要和 Channel 接受端的 Buffer 使用率有关
| outPoolUsage | 发送端 Buffer 的使用率 |
|---|---|
| inPoolUsage | 接收端 Buffer 的使用率 |
| floatingBuffersUsage | 接收端 Floating Buffer 的使用率 |
| exclusiveBuffersUsage | 接收端 Exclusive Buffer 的使用率 |
其中 inPoolUsage = floatingBuffersUsage + exclusiveBuffersUsage。
(1)根据指标分析反压
分析反压的大致思路是:如果一个 Subtask 的发送端 Buffer 占用率很高,则表明它 被下游反压限速了;如果一个 Subtask 的接受端 Buffer 占用很高,则表明它将反压传导 至上游。反压情况可以根据以下表格进行对号入座
| outPoolUsage 低 | outPoolUsage 高 | |
|---|---|---|
| inPoolUsage 低 | 正常 | 被下游反压,处于临时情况 (还没传递到上游)/可能是反压的根源,一条输入 多条输出的场景 |
| inPoolUsage 高 | 如果上游所有 outPoolUsage 都是低,有可能最终可能导致反压(还没传递到上游)/如果上游的 outPoolUsage 是高,则为反压根源 | 被下游反压 |
(2)可以进一步分析数据传输
根据 floatingBuffersUsage/exclusiveBuffersUsage 以 及其上游 Task 的 outPoolUsage 来进行进一步的分析一个 Subtask 和其上游 Subtask 的数据传输。
在流量较大时,Channel 的 Exclusive Buffer 可能会被写满,此时 Flink 会向 BufferPool 申请剩余的 Floating Buffer。这些 Floating Buffer 属于备用 Buffer。
| exclusiveBuffersUsage 低 | exclusiveBuffersUsage 高 | |
|---|---|---|
| floatingBuffersUsage 低 所有上游 outPoolUsage 低 | 正常 | |
| floatingBuffersUsage 低 上游某个 outPoolUsage 高 | 潜在的网络瓶颈 | |
| floatingBuffersUsage 高 所有上游 outPoolUsage 低 | 最终对部分 inputChannel 反压(正在传递) | 最终对大多数或所有inputChannel反压(正在传递) |
| floatingBuffersUsage 高 上游某个 outPoolUsage 高 | 只对部分 inputChannel 反压 | 对大多数或者所有inputChannel反压 |
反压的原因及处理
注意:反压可能是暂时的,可能是由于负载高峰、CheckPoint 或作业重启引起的数据 积压而导致反压。如果反压是暂时的,应该忽略它。另外,请记住,断断续续的反压会影响 我们分析和解决问题。
定位到反压节点后,分析造成原因的办法主要是观察 Task Thread。按照下面的顺序, 一步一步去排查。
查看数据是否有倾斜
在实践中,很多情况下的反压是由于数据倾斜造成的,这点我们可以通过 Web UI 各个 SubTask 的 Records Sent 和 Record Received 来确认,另外 Checkpoint detail 里不同 SubTask 的 State size 也是一个分析数据倾斜的有用指标。
使用火焰图分析
如果不是数据倾斜,最常见的问题可能是用户代码的执行效率问题(频繁被阻塞或者性 能问题),需要找到瓶颈算子中的哪部分计算逻辑消耗巨大。
最有用的办法就是对 TaskManager 进行 CPU profile,从中我们可以分析到 Task Thread 是否跑满一个 CPU 核:如果是的话要分析 CPU 主要花费在哪些函数里面;如果 不是的话要看 Task Thread 阻塞在哪里,可能是用户函数本身有些同步的调用,可能是 checkpoint 或者 GC 等系统活动导致的暂时系统暂停。
(1)开启火焰图 rest.flamegraph.enabled: true #默认 false
(2)查看火焰图
火焰图是通过对堆栈跟踪进行多次采样来构建的。每个方法调用都由一个条形表示,其 中条形的长度与其在样本中出现的次数成正比。
On-CPU: 处于 [RUNNABLE, NEW]状态的线程
Off-CPU: 处于 [TIMED_WAITING, WAITING, BLOCKED]的线程,用于查看在样本中发现的阻塞调用。
(3)分析火焰图
颜色没有特殊含义,具体查看:
纵向是调用链,从下往上,顶部就是正在执行的函数
横向是样本出现次数,可以理解为执行时长。
看顶层的哪个函数占据的宽度最大。只要有"平顶"(plateaus),就表示该函数可能存在性能问题。
外部组件交互
如果发现我们的 Source 端数据读取性能比较低或者 Sink 端写入性能较差,需要检 查第三方组件是否遇到瓶颈,还有就是做维表 join 时的性能问题。
例如:
Kafka 集群是否需要扩容,Kafka 连接器是否并行度较低
HBase 的 rowkey 是否遇到热点问题,是否请求处理不过来 ClickHouse 并发能力较弱,是否达到瓶颈
......
关于第三方组件的性能问题,需要结合具体的组件来分析,最常用的思路:
1)异步 io+热缓存来优化读写性能
2)先攒批再读写
当前现有业务建议使用本地缓存+预加载维表+定时刷新 或者 本地缓存+定时刷新
数据的倾斜
判断是否数据倾斜
相同 Task 的多个 Subtask 中,个别 Subtask 接收到的数据量明显大于其他 Subtask 接收到的数据量,通过 Flink Web UI 可以精确地看到每个 Subtask 处理了多 少数据,即可判断出 Flink 任务是否存在数据倾斜。通常,数据倾斜也会引起反压。
另外, 有时 Checkpoint detail 里不同 SubTask 的 State size 也是一个分析数据倾 斜的有用指标。
使用localkeyby思想
在 keyBy 上游算子数据发送之前,首先在上游算子的本地对数据进行聚合后,再发送 到下游,使下游接收到的数据量大大减少,从而使得 keyBy 之后的聚合操作不再是任务的 瓶颈。类似 MapReduce 中 Combiner 的思想,但是这要求聚合操作必须是多条数据或 者一批数据才能聚合,单条数据没有办法通过聚合来减少数据量。从 Flink LocalKeyBy 实 现原理来讲,必然会存在一个积攒批次的过程,在上游算子中必须攒够一定的数据量,对这 些数据聚合后再发送到下游。
实现方式:
DataStreamAPI 需要自己写代码实现
SQL 可以指定参数,开启 miniBatch 和 LocalGlobal 功能