这是我参与「第四届青训营 」笔记创作活动的第四天
流计算中的Window机制
概述
-
流式和批式
-
批处理
批处理的典型数仓架构为T+1架构,即数据计算是天级别的,当天只能看到前一天的计算结果。
通常使用计算引擎为Hive或Spark等,计算的时候,数据是完全准备好的,输入和输出都是确定性的。
- 小时批处理
原本一天收集一次数据进行计算,改为每小时收集一次数据进行计算,以此追求更实时的数据。但由于收集处理的过程是一个周期调度任务,往往很难实现
- 处理时间的窗口
实时计算:处理时间的窗口
数据实时流动,实时计算,窗口直接发送结果,不需要周期调度任务
- 处理时间VS事件时间
处理时间:数据在流式计算系统中真正处理的时间
事件时间:数据产生的时间,比如客户端,传感器,后端代码上报数据的时间。不稳定,可能随用户所在地的网络环境的变化而变化
理想状态下处理时间和事件时间是同一时间,但现实中处理时间通常比事件时间有一定的延迟。
- 事件时间的窗口
实时计算:事件时间的窗口
数据实时进入到真实事件发生的窗口中去计算,可以有效的处理数据延迟和乱序
问题:不知道窗口什么时候结束
- Watermark
在数据中插入Watermark,表示当前真实时间
在数据乱序时,watermark很重要,可以用来*乱序容忍和实时性之间做一个平衡
Watermark
- 什么是Watermark?
表示 系统认为的当前真实的事件时间
- 如何产生Watermark
creat table Orders(
user BIGINT,
product string,
order_time timestamp(3),
watermark for order_time AS order_time_interval '5' second )with(....);
即从原始数据里面order_time字段减去5秒作为watermark数值
-
如何传递Watermark(取上游所有subtask的最小值)
-
典型问题1
per-partition VS per-subtask watermark
per-subtask watermark(每个子任务):如果一个source消费多个分区,那么多个分区之间的数据读取可能会加剧乱序程度
per-partition(每个分区):基于每个分区单独的watermark机制,可以有效避免上述问题
- 典型问题 2
部分partition/subtask断流
根据watermask传递机制,下游subtask会将上游所有subtask的watermask值的最小值作为自身的watermask值,如果上游有一个subtask的watermark不更新了,则下游的watermark就都不更新,即发生断流
解决:Idle source
当某个subtask断流超过idle的超时时间时,将当前的subtask设置为idle,并下发一个idle状态给下游,下游在计算自身的watermark时,自动忽略当前是idle的subtask
- 典型问题3
迟到数据的处理
watermark是表示当前事件发生的真实时间,晚于watermark的数据就是迟到数据
算子自身决定如何处理迟到的数据
- window聚合:默认丢弃
- 双流join,如果是outer join,则可以认为它不能join到任何数据
- CEP:默认丢弃
Window
基本概念
- Window分类(最典型的三种)
- Tumble Window(滚动窗口)
- Slinding Window(滑动窗口)
- Session Window(会话窗口)
- Window的使用
SQL API:
select
user,
tumble_start(order_time,interval'1'day)as wStart,
sum(amount)from Orders
group by
tumble (order_time,interval '1'day),--在聚合的地方加一个窗口
user
-
滚动窗口
窗口划分:
- 每个key单独划分
- 每条数据 只属于一个窗口
窗口触发:窗口结束的时候一次性触发
- 滑动窗口
和滚动窗口的区别:每条数据可能属于多个窗口
触发:结束时触发
- 会话窗口
和前两者的区别是:一条数据到来时不知道它所属的窗口,每条数据会单独划分一个窗口,如果窗口之间有交集,则会对两个窗口进行合并
触发:结束时触发
迟到数据处理
- 迟到定义:一条数据到来时,会被划分到一个窗口,如果窗口结束时间比当前的watermark的值还小,说明该窗口已经结束,触发完计算了,则该数据迟到
- 只有事件时间会产生迟到数据(处理时间来了就直接算)
- 迟到数据的默认处理:丢弃
-
处理方法1:Allow lateness
- 设置一个允许迟到时间,窗口结束后会多保留allow lateness的时间再清理状态,如果这段时间数据来了,就继续之前的状态计算
- 适用于SQL、datastream
-
SideOutput流
- 对迟到数据打一个标签,然后datastream上根据标签获取到迟到的数据流,然后处理
- 适用于datastream
增量计算VS全量计算
- 增量计算:来一条算一条,window只存储计算结果
- 全量计算:来一条先存一条,存到window的state中,等到window触发时,拿出来一起算
EMIT触发
-
为什么需要emit:通常window只有结束后才可以输出结果,如果窗口很大,1h甚至1天,结果输出的延迟就很高,就失去了实时计算的意义
-
EMIT:在window没有结束的时候,提前把window计算的部分结果输出出来
-
在datastream实现
- 自定义trigger
-
在SQL实现(配置)
Window-高级优化
-
Mini-batch优化:优化解决频繁访问状态问题
问题:①中间结果偏多②状态访问频繁-
原理:先攒一批数据,然后输出
-
-
倾斜优化:优化解决倾斜问题
-
distinct:状态复用降到状态量
-
pane优化:降低滑动窗口的状态存储量
-
问题:在滑动窗口中,一条数据可能属于多个窗口,这样计算量可能会很大
-
解决:把原来的一个窗口划分为更小的几个窗口
-
案例分析
使用flink SQL计算抖音的日活曲线
- 方法:做一个滚动窗口,用emit提前把窗口内容输出
- 问题:所有数据都需要在一个subtask中完成窗口计算,无法进行
计算大数据任务时的资源使用
\