这是我参与「第四届青训营」笔记创作活动的第六天。
window机制
Window是一种将事件按照时间等因素进行分组处理的机制
时间窗口
- 滚动窗口:滚动窗口之间不重叠,数据按key分组
- 滑动窗口:滑动窗口
计数窗口:依据到达元素的先后管理窗口,结果具有不确定性。
会话窗口:需设置Session Gap,超过Session Gap的时间没有新的数据流入则窗口关闭,故其大小可变。
水印是嵌在流中的常规记录,计算程序通过水印获知某个时间点已到。
样例:
DataStream<T> input = ...
// 基于Event Time的滑动窗口
input
.keyBy(<KeySelector>)
.window(SlidingEventTimeWindows.of(Time.seconds(10), Time.seconds(5)))
.<window function>(...)
// 基于Processing Time的滑动窗口
input
.keyBy(<KeySelector>)
.window(SlidingProcessingTimeWindows.of(Time.seconds(10), Time.seconds(5)))
.<window function>(...)
// 在滑动窗口上设置Offset偏移
input
.keyBy(<KeySelector>)
.window(SlidingProcessingTimeWindows.of(Time.hours(12), Time.hours(1), Time.hours(-8)))
.<window function>(...)
迟到数据处理
Allow lateness
设置允许迟到的时间,窗口结束后保留相应时间的状态,若有新的数据到来,则继续根据之前的状态开始计算
适用:DataStream、SQL
Sideoutput 侧输出流
为迟到数据打上tag,DataStream根据tag获取迟到数据流,业务层面自行选择处理
适用:DataStream
增量计算
每条数据到来,直接进行计算,只维护中间状态,window只存储结果
典型如:reduce、aggregate等函数
SQL的聚集函数只有增量计算
全量计算
每条数据到来,存入状态中,window触发计算时取出一起计算,如process函数
EMIT触发
EMIT指在window未结束的时候提前把部分计算结果输出
在DataStream中可以自定义trigger来实现,结果有:
- CONTINUE 什么都不做。
- FIRE 触发计算,但不清理
- PURGE 清理窗口数据但不执行计算。
- FIRE_AND_PURGE 启动计算,发送结果然后清理窗口数据。
优化
mini-batch优化 减少了数据序列化反序列化次数
通过复用底层的watermark机制,将watermark作为划分mini-batch 的依据
local-global 倾斜优化
先进行预处理,再传递给下游,以降低数据shuffle的量 主要过程是先进行一个local的aggregate,再发送到global的aggregate算子。
DISTICT 计算状态复用
对同一个字段用不同的filter来进行count distinct的计算时如果每个指标都单独用一个map来记录每条数据是否出现过,状态量会变得很大。
通过将相同字段的distinct计算用一个map的key来存储,在map的value中,用一个bit vector来实现就可以把各个状态复用到一起。如一个bigint有64位,可以表示同一个字段的64个filter。
pane优化 降低计算量
对于滑动窗口,将窗口划分成更小粒度的pane,有新的数据流入时更新其对应的pane即可,当window需要输出结果时将各pane的结果merge起来即可。
案例
抖音DAU曲线绘制
主要思路:两阶段查询:先进行一个子查询将用户按uid分入各个桶中,再统计各桶中的活跃用户数量,提高了任务的并行性。