在Flink DataStream中,可以通过Window,将无限的流(Streaming)分割成有限的批(Batch),进而进行各种统计。
本文总结Flink DataStream中Window的分类,以及Window: Tumbling Time Window(基于时间的滚动窗口)
、Sliding Time Window(基于时间的滑动窗口)
、Tumbling Count Window(基于数量的滚动窗口)
、Sliding Count Window(基于数量的滑动窗口)
、Session Window(基于会话间隔的会话窗口)
、Global Window(全局窗口)
的使用。
Window分类
Keyed Window
与Non-Keyed Window
从大的方面,Flink DataStream 中的Window可以分为Keyed Window
与Non-Keyed Window
。
Keyed Window
stream
.keyBy(...) // Keyed Window 和 Non-Keyed Window的区别
.window(...) // 必选: WindowAssigner。将接入的数据分配到不同的窗口。
[.trigger(...)] // 可选: Trigger 触发器。默认为与WindowAssigner关联的默认触发器。
[.evictor(...)] // 可选: Evictor 剔除器。默认无。
[.allowedLateness(...)] // 可选: AllowedLateness 迟到间隔。默认0。
[.sideOutputLateData(...)] // 可选: 迟到数据侧输出。默认无,即默认丢掉迟到的数据。
.reduce/process/aggregate/fold/apply() // 必选: Window Function
Non-Keyed Window
stream
.windowAll(...) // 必选: WindowAssigner。将接入的数据分配到不同的窗口。
[.trigger(...)] // 可选: Trigger 触发器。默认为与WindowAssigner关联的默认触发器。
[.evictor(...)] // 可选: Evictor 剔除器。默认无。
[.allowedLateness()] // 可选: AllowedLateness 迟到间隔。默认0。
[.sideOutputLateData(...)] // 可选: 迟到数据侧输出。默认无,即默认丢掉迟到的数据。
.reduce/process/aggregate/fold/apply() // 必选: Window Function
区别与联系
- 关系上:
Non-Keyed Window
实际上是Keyed Window
的一个特例。Non-Keyed Window = Keyed Window . keyBy(NullByteKeySelector)
,即将原始数据流按相同的Key(byte 0
)分到同一个Task上。 - 语法上:
Keyed Window
需要keyBy
,通过window
指定窗口。Non-Keyed Window
没有keyBy
,通过windowAll
指定窗口。 - 并行度:
Keyed Window
,基于key对原始数据流进行逻辑分组,每个逻辑Keyed Stream
和其他Keyed Stream
完全独立,同一个Key的数据会被分到同一个Task中,即最终窗口计算可在多个Task上并行执行。Non-Keyed Window
,原始流不会被逻辑分组,并行度为1,最终只会在一个Task中执行。
Time Window
与 Count Window
Flink DataStream根据WindowAssigner
将接入的数据分配到不同的窗口。根据WindowAssigner
分配方式的不同,可将Winow分为2大类和4小类。2大类: 时间窗口(Time Window
)、数量窗口(Count Window
)。4小类: 全局窗口(Global Window
)、滚动窗口(Tumbling Window
)、滑动窗口(Sliding Window
)、会话窗口(Session Window
)。
时间窗口Time Window
- 全局窗口(
Global Window
),在Keyed Window
和Non-Keyed Window
中均可使用。 - 滚动窗口(
Tumbling Window
),在Keyed Window
和Non-Keyed Window
中均可使用。 - 滑动窗口(
Sliding Window
),在Keyed Window
和Non-Keyed Window
中均可使用。 - 会话窗口(
Session Window
),在Keyed Window
和Non-Keyed Window
中均可使用。
数量窗口Count Window
- 全局窗口(
Global Window
),在Keyed Window
和Non-Keyed Window
中均可使用。 - 滚动窗口(
Tumbling Window
),在Keyed Window
和Non-Keyed Window
中均可使用。 - 滑动窗口(
Sliding Window
),在Keyed Window
和Non-Keyed Window
中均可使用。
以下总结几个常用Keyed Window
的使用。
基于时间的滚动窗口(Tumbling Time Window
)
keyedStream.window(TumblingEventTimeWindows.of(size))
# 快捷方式
keyedStream.timeWindow(Time size)
# 示例: 每隔5秒统计每个商品的点击次数
stream
.keyBy("productID")
.timeWindow(Time.seconds(5))
.process(new MyCustomTimeWindowProcessFunction())
.print();
基于时间的滑动窗口(Sliding Time Window
)
keyedStream.window(SlidingEventTimeWindows.of(size, slide))
# 快捷方式
keyedStream.timeWindow(Time size, Time slide) 如:
# 示例: 每隔5秒统计最近10内每个商品的点击次数
stream
.keyBy("productID")
.timeWindow(Time.seconds(10),Time.seconds(5))
.process(new MyCustomTimeWindowProcessFunction())
.print();
基于数量的翻滚窗口(Tumbling Count Window
)
keyedStream.window(GlobalWindows.create()).trigger(PurgingTrigger.of(CountTrigger.of(size)))
# 快捷方式: keyedStream.countWindow(long size)
# 示例: 按商品分组, 每5个事件就统计一次点击的UV
stream
.keyBy("productID")
.countWindow(5)
.process(new MyCustomCountWindowProcessFunction())
.print();
基于数量的滑动窗口(Sliding Count Window
)
keyedStream.window(GlobalWindows.create()).evictor(CountEvictor.of(size)).trigger(CountTrigger.of(slide))
# 快捷方式
keyedStream.countWindow(long size, long slide)
# 示例: 按商品分组, 每5个事件就统计一次最近10个事件中点击的UV
stream
.keyBy("productID")
.countWindow(10,5)
.process(new MyCustomCountWindowProcessFunction())
.print();
基于会话间隔的会话窗口(Session Window
)
keyedStream.window(EventTimeSessionWindows.withGap(size))
# 示例: 统计一个用户在一个Session中浏览了多少商品
# 注意: 这里的Session Gap是5秒,含义为: 5秒钟内,改用户不活跃,则认为窗口结束,可以触发窗口计算了。
stream
.keyBy("userID")
.window(EventTimeSessionWindows.withGap(Time.seconds(5)))
.process(new MyCustomSessionWindowProcessFunction())
.print();
注意:
Session Window
将一段时间内的持续事件放到一个窗口。Session Window
和其他的Time Window
不同,Session Window
没有固定的起止时间。根据Session Gap
触发窗口计算。
全局窗口(Global Window
)
在Keyed Window
中,Global Window
将相同Key的数据放到同一个窗口。窗口没有明确的起止时间,需要借助于Triger
来触发窗口计算。因此,使用Global Window
必须指定对应的触发器。具体可参考Count Window
的实现。
keyedStream.window(GlobalWindows.create()).trigger(...)
# 示例:
stream
.keyBy("userID")
.window(GlobalWindows.create())
.trigger(new MyCustomGlobalWindowTriger())
.process(new MyCustomGlobalWindowProcessFunction())
.print()