Flink 中的窗口 Window 机制 | 青训营笔记

186 阅读3分钟

image.png

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


一、Flink 中的窗口 Window 机制

Flink 底层引擎是一个流式引擎,Flink 基于此实现了流处理和批处理。因此在Flink中,可以将 Batch 认为是一种特殊的 Streaming,而窗口(Window)就是连接 Streaming 和 Batch 的一座桥梁。

二、什么是 Window

窗口(Window)是处理无界流的关键所在。窗口可以将数据流装入大小有限的“”(Bucket)中,再对每个“桶”加以处理。

在流处理系统中数据源源不断流入到系统,我们可以逐条处理流入的数据,也可以按一定规则一次处理流中的多条数据。当处理数据时程序需要知道什么时候开始处理、处理哪些数据。窗口提供了这样一种依据,决定了数据何时开始处理。

三、窗口分类

窗口可以是时间驱动的(Time Window,例如:每30秒钟),也可以是数据驱动的(Count Window,例如:每一百个元素)。一种经典的窗口分类可以分成:翻滚窗口(Tumbling Window,无重叠)滚动窗口(Sliding Window,有重叠),和会话窗口(Session Window,活动间隙)

1. 滚动窗口(Tumbling Windows)

滚动窗口的 assigner 分发元素到指定大小的窗口。滚动窗口的大小是固定的,且各自范围之间不重叠。 比如说,如果你指定了滚动窗口的大小为 5 分钟,那么每 5 分钟就会有一个窗口被计算,且一个新的窗口被创建(如下图所示)。
image.png

滚动窗口能将数据流切分成不重叠的窗口,每一个事件只能属于一个窗口,Window结束时间到达的时候一次性触发。

通过使用 DataStream API,我们可以这样实现:

DataStream<T> input = ...;

// 滚动 event-time 窗口  
input.keyBy(<key selector>)  
    .window(TumblingEventTimeWindows.of(Time.seconds(5)))  
    .<windowed transformation>(<window function>);

// 滚动 processing-time 窗口  
input  
    .keyBy(<key selector>)  
    .window(TumblingProcessingTimeWindows.of(Time.seconds(5)))  
    .<windowed transformation>(<window function>);  

2. 滑动窗口(Sliding Windows)

与滚动窗口类似,滑动窗口的 assigner 分发元素到指定大小的窗口,Window 结束时间到达的时候一次性触发,窗口大小通过 window size 参数设置。 滑动窗口需要一个额外的滑动距离(window slide)参数来控制生成新窗口的频率。 因此,如果 slide 小于窗口大小,滑动窗口可以允许窗口重叠。这种情况下,一个元素可能会被分发到多个窗口。

比如说,你设置了大小为 10 分钟,滑动距离 5 分钟的窗口,你会在每 5 分钟得到一个新的窗口, 里面包含之前 10 分钟到达的数据(如下图所示)。 image.png

通过使用 DataStream API,我们可以这样实现:

DataStream<T> input = ...;

// 滑动 event-time 窗口
input
    .keyBy(<key selector>)
    .window(SlidingEventTimeWindows.of(Time.seconds(10), Time.seconds(5)))
    .<windowed transformation>(<window function>);

// 滑动 processing-time 窗口
input
    .keyBy(<key selector>)
    .window(SlidingProcessingTimeWindows.of(Time.seconds(10), Time.seconds(5)))
    .<windowed transformation>(<window function>);

3. 会话窗口(Session Windows)

会话窗口的 assigner 会把数据按活跃的会话分组。 与滚动窗口滑动窗口不同,会话窗口不会相互重叠,且没有固定的开始或结束时间。 会话窗口在一段时间没有收到数据之后会关闭,即在一段不活跃的间隔之后。 会话窗口的 assigner 可以设置固定的会话间隔(session gap)或 用 session gap extractor 函数来动态地定义多长时间算作不活跃。 当超出了不活跃的时间段,当前的会话就会关闭,并且将接下来的数据分发到新的会话窗口。

image.png

通过使用 DataStream API,我们可以这样实现:

DataStream<T> input = ...;

// 设置了固定间隔的 event-time 会话窗口
input
    .keyBy(<key selector>)
    .window(EventTimeSessionWindows.withGap(Time.minutes(10)))
    .<windowed transformation>(<window function>);
    
// 设置了动态间隔的 event-time 会话窗口
input
    .keyBy(<key selector>)
    .window(EventTimeSessionWindows.withDynamicGap((element) -> {
        // 决定并返回会话间隔
    }))
    .<windowed transformation>(<window function>);

// 设置了固定间隔的 processing-time session 窗口
input
    .keyBy(<key selector>)
    .window(ProcessingTimeSessionWindows.withGap(Time.minutes(10)))
    .<windowed transformation>(<window function>);
    
// 设置了动态间隔的 processing-time 会话窗口
input
    .keyBy(<key selector>)
    .window(ProcessingTimeSessionWindows.withDynamicGap((element) -> {
        // 决定并返回会话间隔
    }))
    .<windowed transformation>(<window function>);

4. 其它窗口

  • 全局窗口(Global Windows)
  • 计数窗口(Count Windows)
  • 累计窗口(Cumulate Windows)
  • ......