流式计算中的 Window 计算笔记(二)| 青训营笔记
这是我参与「第四届青训营 -大数据场」笔记创作活动的第6天
三、Window概述
1. window基本功能
-
window分类
- tumble window 滚动窗口
- sliding window 滑动窗口
- session window 会话窗口
- 其他window:全局window、count window、累计窗口等
-
window使用
1.1 tumble window 滚动窗口
- 窗口划分:每个key单独划分;每条数据只属于一个窗口
- 窗口触发:window结束时间到达时一次性触发
1.2 HOP Sliding window 滑动窗口
- 窗口划分:每个key单独划分;每条数据可能属于多个窗口
- 窗口触发:window结束时间到达时一次性触发
1.3 session window 会话窗口
-
窗口划分:每个key单独划分;每条数据会单独划分一个窗口,如果window之间有交集,则对窗口进行merge;
- session窗口划分是动态的,可能会与之前的窗口合并
-
窗口触发:window结束时间到达时一次性触发
1.4 迟到数据处理
- 迟到:一条数据到来后会用window assigner给它划分一个window,一般时间窗口是一个时间区间,如果划分出来的window end比当前的watermark小,说明这个窗口已经触发计算了,这条数据就是迟到数据。
- 什么情况下会产生迟到数据:只有事件时间下才会有迟到的数据。
- 迟到数据的默认处理:丢弃
1.4.1 allow lateness
需要设置一个允许迟到的时间。设置之后窗口正常计算结束后不会马上清理状态,而是会多保留allow lateness这么长的时间。在这段时间内如果还有数据到来则继续之前的状态进行计算。
- 适用:DataStream、SQL
1.4.2 SideOutput 侧输出流
这种方式对迟到数据打一个tag,然后在dataStream上根据这个tag获取迟到数据流,然后业务层自行选择进行处理。
- 适用于 DataStream
1.5 增量计算和全量计算
-
增量计算:每条数据到来直接进行计算
- window只存储计算结果,不保留每条数据
- 典型的reduce、aggregate等函数都是增量计算
- SQL中聚合只有增量计算
-
全量计算:每条数据到来后会存储到window的state中
- window存储所有数据,触发计算时将所有数据拿出一起计算
- 典型的process函数就是全量计算
1.6 EMIT触发
1.6.1 什么是EMIT
- 通常window都是在结束时才输出结果。如果窗口比较大则计算结果输出的延迟就比较高,失去了实时计算的含义。
- EMIT输出指的是在window没有结束时提前把window计算的部分结果输出出来。
1.6.2 怎么实现EMIT
-
DataStream中通过自定义Trigger来实现
-
Trigger的结果可以是
- CONTINUE
- FIRE(触发计算但不清理)
- PURGE
- FIRE_AND_PURGE
-
-
SQL通过配置:
- table.exec.emit.early-fire.enabled=true
- table.exec.emit.early-fire.delay=(time)
1.7 window offset
windowStart = timeStamp - (timeStamp - offset + windowSize)% windowSize
可以在计算窗口时让窗口有一个偏移。
- DataStream支持offset,SQL不支持。
1.8 小结
- 三种(滚动、滑动、会话)窗口的定义
- 迟到数据处理:AllowLateness、SideOutput
- 增量计算和全量计算模型
- EMIT触发提前输出窗口的结果
2. window高级优化
2.1 mini-batch优化
- RETRACT机制下中间结果偏多输出量大,内存存储到外部状态需要序列化和反序列化消耗CPU
- 多条数据进行一次状态读取、序列化和反序列化、以及写入过程 (攒一批数据然后输出)
- 上下游算子如果都缓存一批进行输出,数据延迟会大大增加
- 利用类watermark传递机制,上游添加mini-batch assigner算子,发送开始mini-batch的信号;下游收到信号后开始对缓存数据进行计算,同时向下游继续发送;
2.2 local-global 倾斜优化
- 在shuffle之前,先进行local预聚合,再shuffle到下游全局算子,解决数据倾斜的热点问题
2.3 distinct状态复用
- DISTINCT通常优化为Group by等形式
- 利用FILTER前置过滤DISTINCT
- 复用DISTINCT,把key对应为bit value来确定状态(是否有对应key),从而通过filter复用;
- 降低DISTINCT连续数据的存储量。
2.4 滑动窗口pane复用
- 滑动窗口每条数据参与多个窗口,数据开销比较大
- 把滑动窗口划分成更小的pane,从而变成类滚动窗口的形式;每个滑动窗口就是多个pane的聚合,从而数据在每个pane中可以节省访问,降低计算成本。
- 输出时需要临时做一个默指。
2.5 小结
- Mini-batch优化解决频繁访问状态的问题
- local-global优化解决倾斜问题
- Distinct状态复用降低状态量
- Pane优化降低滑动窗口的状态存储量
四、案例分析
1. 计算抖音的日活曲线
所有数据都需要在一个subtask中完成窗口计算,无法并行。
SELECT
SUM(partial_cnt) as dau
TUMBLE_START(event_time, INTERVAL '1' DAY) as wstart,
LOCALTIMESTAMP as current_ts
FROM(
SELECT
COUNT(DISTINCT uid) as partial_cnt,
TUMBLE_ROWTIME(event_time, INTERVAL '1' DAY) as event_time
FROM user_activity
GROUP BY
TUMBLE(event_time, INTERVAL '1' DAY),
MOD(uid,10000) -- 根据uid分为10000个桶
)
GROUP BY TUMBLE(event_time, INTERVAL '1' DAY)
通过两阶段聚合来把数据打散,完成第一轮聚合,第二轮聚合只需要对各个分桶的结果求和即可。
2. 计算大数据任务的资源使用
根据YARN上报的各个container的信息,在任务结束时尽快计算出一个任务占据的总资源,假设前后两个container间隔时间不足10min;
- 典型通过会话窗口将数据划分到一个window中 然后将结果求和
SELECT application_id,
SUM(cpu_usage) as CPU_TOTAL
SUM(memory_usage) as MEMORY_TOTAL
FROM resource_usage
GROUP BY
application_id,
SESSION(event_time, INTERVAL '10' MINUTE)
课程总结
- 第一部分介绍了流式计算基本概念,以及和批式计算的区别
- 第二部分介绍了watermark的含义、如何生成、如何传递,以及如何处理部分partition断流的问题
- 第三部分介绍了三种基本的window的定义,以及迟到数据处理、增量计算VS全量计算、EMIT输出;同时也介绍了local-global优化、mini-batch优化、distinct状态优化、滑动窗口的pane的优化等
- 两个案例介绍滚动窗口、会话窗口,以及两阶段聚合解决倾斜问题