这是我参与「第四届青训营 」笔记创作活动的第2天
概述
实时计算:处理时间窗口
数据实时流动,实时计算,窗口结束直接发送结果,不需要周期调度任务
处理时间 vs 事件时间
- 处理时间:数据在流式计算系统中真正处理时所在机器的当前时间
- 事件时间:数据产生的时间,比如客户端、传感器、后端代码等上报数据时的时间
事件时间窗口
实时计算:事件时间窗口
数据实时进入到真实事件发生的窗口中进行计算,可以有效地处理数据延迟和乱序
什么时候窗口才算结束?
-> watermark
在数据中插入一些表示当前真实时间的watermark。当数据存在乱序时,watermark用来在乱序容忍和实时性之间做一个平衡
Watermark
是什么?
系统认为的当前真实的事件时间
如何产生?
一般从数据的事件时间来产生。常见的包括使用当前事件时间减去一个固定的delay,来表示可以容忍多长时间的乱序
如何传递?
上下游task之间有数据传输关系的,上游就会将watermark传递给下游 下游收到多个上游传递过来的watermark后,默认取其中最小值作为自身的watermark,同时传递给它的下游。 经过整个传递过程,最终系统中每一个计算单元都会实时的知道自身当前的watermark是多少。
问题1:per-partition vs per-subtask watermark 生成
-
Flink里早期是per-subtask机制。典型问题是 如果一个source task消费多个partition,那么多个partition之间的数据可能会因为消费的速度不同,加剧数据的乱序程度。
-
新版本引入了每个partition单独的watermark生成机制,可以有效避免以上情况
问题2:部分partition/subtask断流
如果上游有一个subtask的watermark不更新了,则下游的watermark都不更新。
-> 解决方式:idle source
当某个subtask断流超过配置都idle超时时间,将当前的subtask设置为idle,并下发一个idle状态给下游。下游在计算自身watermark时,可以忽略掉当前是idle的subtask。
问题3:迟到数据处理
算子自身来决定如何处理迟到数据
- Window聚合,默认会丢弃迟到数据
- 双流join,如果是outer join,则可以认为它不能join到任何数据
- CEP,默认丢弃
window
基本功能
典型的window
- tumble window 滚动窗口
- 窗口划分:每个key单独划分,每条数据只会属于一个窗口
- 窗口触发:window结束时间到达的时候一次性触发
- sliding window 滑动窗口
- 窗口划分:每个key单独划分,每条数据可能会属于多个窗口
- 窗口触发:window结束时间到达的时候一次性触发
- session window 会话窗口
- 窗口划分:每个key单独划分,每条数据会单独划分为一个窗口,如果window之间有交集,则会对窗口进行merge
- 窗口触发:window结束时间到达的时候一次性触发
迟到数据处理
- allow lateness:设置一个允许迟到的时间。窗口正常计算结束后,不会马上清理状态,而是会多保留 allow lateness 这么长的时间。在这段时间如果还有数据到来,继续之前的状态进行计算
- side output(侧输出流):对迟到的数据打个tag,在DataStream上根据这个tag获取迟到数据流,然后业务层面自行选择进行处理
增量 vs 全量计算
- 增量计算:每条数据到来,直接计算,window只存储计算结果(e.g. reduce、aggregate函数)
- 全量计算:每条数据到来,会存储到window到state,等到窗口触发计算的时候,才把所有数据拿出来一起计算(e.g. process函数)
EMIT触发
- 什么是EMIT?通常窗口的结果在结束的时候才会。如果窗口比较大,就失去了实时计算的意义。EMIT输出是指,在window还没有结束时,提前把window计算的部分结果输出出来。
- 怎么实现?在DataStream里可以通过自定义trigger来实现。trigger的结果可以是continue、fire、purge、fire_and_purge