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

58 阅读4分钟

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

概述

实时计算:处理时间窗口

数据实时流动,实时计算,窗口结束直接发送结果,不需要周期调度任务

处理时间 vs 事件时间

  • 处理时间:数据在流式计算系统中真正处理时所在机器的当前时间
  • 事件时间:数据产生的时间,比如客户端、传感器、后端代码等上报数据时的时间

事件时间窗口

实时计算:事件时间窗口

数据实时进入到真实事件发生的窗口中进行计算,可以有效地处理数据延迟和乱序

什么时候窗口才算结束?

-> watermark

在数据中插入一些表示当前真实时间的watermark。当数据存在乱序时,watermark用来在乱序容忍和实时性之间做一个平衡


Watermark

是什么?

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

如何产生?

一般从数据的事件时间来产生。常见的包括使用当前事件时间减去一个固定的delay,来表示可以容忍多长时间的乱序

如何传递?

image.png

上下游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结束时间到达的时候一次性触发

迟到数据处理

  1. allow lateness:设置一个允许迟到的时间。窗口正常计算结束后,不会马上清理状态,而是会多保留 allow lateness 这么长的时间。在这段时间如果还有数据到来,继续之前的状态进行计算
  2. 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