反压
反压通常产生于短时间的流量高峰导致系统接收数据的速率远高于处理数据的速率。例如:垃圾回收停顿可能导致流入的数据快速堆积;大促、秒杀活动导致的流量突增。
反压机制下系统能够自己检测到被阻塞的Operator,然后自适应的降低上游或者源头的数据发送速率,从而维持整个系统的稳定性。
引起反压的原因
- cpu或内存系统资源不足
- 长时间垃圾收集(GC)
- CUP/线程瓶颈
- 线程竞争,subTask上可能会因为共享资源上高负载线程的竞争而形成瓶颈
- 数据倾斜等引起的负载不平衡
- 外部依赖,如source端读取或者sink端写入性能较差
数据倾斜
数据倾斜是指相同Task中的多个SubTask中,个别SubTask接收的数据量明显大于其他SubTask接收到的数据量。通常,数据倾斜会产生反压。
数据倾斜解决办法
-
keyBy之前发生数据倾斜
如果keyBy之前就存在数据倾斜,上游算子的某些实例可能处理的数据较多,某些实例处理的数据较少,产生该情况可能是因为数据源的数据本身就不均匀,例如由于某些原因Kafka的topic中某些partition的数据量比较大,某些partition的数据量比较少。对于不存在keyBy的Flink任务也会出现数据倾斜的情况。
这种情况,需要让Flink任务强制shufle。使用shuffle、rebalance或rescale算子即可将数据均匀分配,从而解决数据倾斜问题。
-
keyby后的聚合操作存在数据倾斜
使用LocalKeyBy的思想: 在keyBy上游算子数据发送之前,首先在上游算子的本地对数据进行聚合后再发送到下游,使下游接收到的数据量大大减少,从而使得keyBy之后的聚合操作不再是任务的瓶颈。但是这要求聚合操作必须是多条数据或者一批数据才能聚合,单条数据没有办法通过聚合来减少数据量。从Flink LocalKeyBy实现原理来讲,必然会存在一个积攒批次的过程,在上游算子中必须攒够一定的数据量,对这些数据聚合后再发送到下游。
注意: Flink实时流处理,如果keyby之后的聚合操作存在数据倾斜,且没有开窗口的情况下,简单的认为使用两阶段聚合,是不能解决问题的。因为这个时候Flink是来一条处理一条,且向下游发送一条结果,对于原来keyby的维度来讲,数据量并没有减少,且结果重复计算。如下图:
实现方式: keyby之前使用flatMap实现LocalKeyBy。当数据达到x条或者x时间后,批次数据保存在状态后端,聚合一次后输出一条。
-
keyby后的窗口聚合操作存在数据倾斜
因为使用了窗口,变成了有界数据的处理,窗口默认时触发关窗时才会输出一条结果发往下游,所以可以使用两阶段聚合的方式。
实现思路:
第一阶段聚合:key拼接随机数前缀或后缀,进行keyby、开窗、聚合。聚合完不再是WindowedStream,要获取WindowEnd作为窗口标记作为第二阶段分组依据,避免不同窗口的结果聚合到一起。
第二阶段聚合:去掉随机数前缀或后缀,按照原来的key及windowStart/windowEnd作为keyby、聚合。