流计算中的Window计算 | 青训营笔记

74 阅读5分钟

这是我参与「第四届青训营 」笔记创作活动的的第4天

流计算中的Window计算

概述

批处理

  • T+1架构,当天只能看到前一天的计算结果
  • 通常使用的计算引擎为Hive或者Spark,计算的时候,数据是完全ready的,输入和输出都是确定性的

处理时间窗口:窗口结束直接发送结果,不需要周期调度任务

事件时间:数据产生的时间

处理时间:数据在流式计算系统中真正处理时所在机器的当前时间

现实中,处理时间比事件时间有延迟

Watermark:如Watermark为11,左边不应该有小于11的事件,如果有,说明这个事件延迟到达了,不应该带入该次计算中

image-20220727171917344.png

Watermark

Watermark:系统认为的当前真实的事件时间

每个算子的Watermark数值是上游传递的多个Watermark的最小值,同时每个算子也要向下游传递自己的Watermark数值 image-20220727172505375.png

Per-subtask 和 Per-partition

Per-partition:每个partition单独生成watermark,发给source算子后,source算子取最小值

部分partition/subtask断流

上游有一个subtask的watermark不更新,则下游的watermark都不更新

Idle source:某个subtask断流超过idle配置的超时时间,将当前subtask设置为idle,并下发一个idle状态给下游。下游计算时,忽略当前是idle的那些subtask

迟到数据

晚于watermark的数据到来时,系统会认为这种数据是迟到的数据。假设watermark为10,事件时间为9的数据在watermark之后 到达,这就是迟到的数据。

Window

基本功能

滚动窗口、滑动窗口、会话窗口

滚动窗口

有一个属性滚动时间,每到滚动时间窗口滚动一次。假设滚动时间为1h,窗口10点到12点->11点到13点

数据进来时,就可以直接计算。比如求和,一个数据进来后就可以和之前的sum进行累加。窗口的输出只有在窗口结束时才输出。比如窗口范围为10点到11点,只有到了11点才会将窗口数据一次性全部输出。

划分

  • 每个key单独划分。如下图,共有3个user,根据userId单独划分窗口,user1五个,user2五个,user3四个
  • 每条数据只会属于一个窗口

触发

  • 结束时间到达时一次性触发

image-20220727174418163.png

滑动窗口

划分

  • 每个key单独划分
  • 每条数据可能会属于多个窗口(区别于滚动窗口)

触发

  • 结束时间到达时一次性触发 image-20220727175327587.png
会话窗口

当前数据到来时,可能会跟之前的数据的窗口合并。如下图,假设session gap为10min,10:30数据1到达,数据1的窗口为10:30-10:40,10:35数据2到达,跟数据1的窗口合并,此时两个数据属于同一个窗口,窗口为10:30-10:45

划分

  • 每个key单独划分
  • 每条数据单独划分为一个窗口,如果window之间有交集,则会对窗口进行merge(窗口会动态变化,第一条数据到来时无法确定窗口的大小,区别于前两个窗口)

触发

  • 结束时间到达时一次性触发

image-20220727180120909.png

迟到数据处理

一条数据到来后,会用WindowAssigner给它划分一个window,一般时间窗口是一个时间区间,比如[10:00,11:00),如果划分出来的window end比当前的watermark(即当前事件的真实时间)还小,说明这个窗口已经触发过计算了,这条数据会被认为是迟到数据

Allow lateness

设置一个允许迟到的时间。窗口正常计算结束后,不会马上清理状态,而是会多保留allowLatenes这么长的时间,在这段时间内如果还有数据到来,则继续之前的状态进行计算,计算出的结果覆盖之前的结果。

SideOutput

对迟到数据打一个tag,在DataStream上根据tag获取迟到数据流,然后业务层面自行选择进行处理

增量/全量计算

增量计算

  • window只存储计算结果,比如计算sum,状态中只需要春初sum的结果

全量计算

  • 每条数据到来,会存储到window的state中
EMIT触发

在window没有结束的时候,提前把window计算的部分结果输出出来

自定义Trigger来实现

高级优化

Mini-batch优化

image-20220727233214485.png 中间结果偏多,状态变换比较频繁

image-20220727233421420.png 每个算子攒(缓存)一批数据然后再一起输出。

缺点:异常数据流经flink任务的延迟增加,等于上下游每个算子的延迟总和

解决:上游添加一个assigner算子,发出指令发起一个batch信号,算子收到batch后,输出数据,并将batch信号传送给下游。实现在一个时间周期内,所有算子都完成batch的运算

倾斜优化 local-global

image-20220727234015500.png

加一层local计算,防止下游的聚合算子压力过大

Pane优化

滑动窗口变为滚动窗口,输出时进行merge

实际案例

计算抖音的日活曲线

方式一:滚动窗口+EMIT提前输出(实时性)

缺点:计算时需要单并发的程序进行,所有的数据都需要在一个subtask中完成窗口计算,无法并行

方式二:两阶段聚合,第一轮把数据打散,第二轮把各个分桶的结果求和

image-20220728002020253.png

大数据情况下的资源使用

image-20220728002356033.png

会话窗口,session gap设置为10min image-20220728002554123.png