|flink-1.9 docs:Apache Flink 1.9 Documentation: Windows
一、window的理解
1. Flink是一种流式计算引擎,用于处理无界数据流。在处理无界流时,一种有效的方式是将无限的数据切割成有限的"数据块",这就是窗口(Window)的概念。
2. 在Flink中,窗口是用来处理无界流的核心概念。你可以将窗口想象成一个动态创建的数据存储桶,数据源源不断地流入这些存储桶。当某个时间点窗口需要关闭时,它停止收集数据、触发计算并输出结果。
3. 窗口的时间范围以左闭右开的区间表示,例如,0~10秒的窗口表示为[0, 10)。
4. 在处理时间窗口时,窗口的关闭是基于系统时间的,即当数据的时间戳超过窗口结束时间时,窗口关闭并触发计算。对于迟到的数据,它们会被分配给后续窗口,但可能会导致处理结果不准确。
5. 在事件时间语义下,由于可能存在乱序数据,需要设置延迟时间来等待所有数据到达。这可以确保包含迟到的数据并保持准确性。
6. 窗口实际上可以被理解为多个有限大小的“存储桶”,数据会根据时间戳分发到对应的存储桶中。当到达窗口结束时间时,对每个存储桶中的数据进行计算处理。
7. 窗口是动态创建的,只有在有数据到达时才会创建对应的窗口。窗口的触发计算和关闭可以分开执行,确保数据被准确处理。
总之,Flink中的窗口是用来处理无界数据流的重要机制,通过动态创建存储桶和设置事件时间语义来确保数据的正确处理。
二、window的分类
窗口的分类可以按照不同的角度来进行划分:
1. 按照驱动类型分类
1.1时间窗口(Time Window)
这种窗口根据时间段来截取数据。窗口的开始和结束由时间点定义,数据在这个时间段内进行收集和计算。时间窗口可以基于处理时间或事件时间定义。
1.2计数窗口(Count Window)
这种窗口根据数据元素的个数来截取数据。当达到固定的数据个数时,窗口触发计算并关闭。计数窗口不依赖时间,只关注数据个数。
2. 按照窗口分配数据的规则分类
2.1滑动窗口(Sliding Window)
滑动窗口也有固定的大小,但窗口之间可以有一定的重叠。窗口的滑动步长决定了窗口的触发频率。数据可能同时属于多个窗口,取决于窗口大小和滑动步长的比值。
2.2 滚动窗口(Tumbling Window)
滚动窗口有固定的大小,窗口之间没有重叠,数据被均匀切片。数据属于一个窗口,无论何时,窗口之间没有间隔。
2.3 会话窗口(Session Window)
会话窗口基于数据之间的时间间隔来截取数据。如果数据之间的间隔小于指定的时间(会话超时时间),它们属于同一个会话窗口。会话窗口的长度是不固定的,可以根据数据的活动来动态划分。
2.4 全局窗口(Global Window)
全局窗口将相同键(key)的所有数据分配到一个窗口中,没有窗口结束的时间点。触发计算需要自定义触发器。这种窗口通常用于需要更灵活处理的情况,如计数窗口的底层实现就是全局窗口。
Flink中有多种窗口类型可供选择,可以根据不同的需求和数据特性来选择适当的窗口类型。每种窗口类型都有其特定的应用场景和用途。
三、窗口 DataStream api
|flink-1.9 docs:Apache Flink 1.9 Documentation: Windows
在Flink中,窗口操作主要分为按键分区窗口(Keyed Windows)和非按键分区窗口(Non-Keyed Windows),具体如下:
1. 按键分区窗口(Keyed Windows): 在按键分区窗口中,数据流通过.keyBy()操作被按键分区成多个逻辑流(KeyedStream),每个逻辑流都对应一个特定的键(key)。窗口计算会在多个并行子任务上同时执行,相同键的数据会被发送到同一个并行子任务上进行窗口计算。这意味着每个键都有一组独立的窗口进行统计计算。
在代码中,按键分区窗口操作的典型调用顺序如下:
//1、使用.keyBy()按键分区,将数据流按照键分成多个逻辑流。
stream.keyBy(<key selector>)
//2、使用.window()定义窗口,传入一个窗口分配器(Window Assigner),该分配器指定了窗口的类型和属性。
.window(<window assigner>)
//3、使用.aggregate()或其他窗口函数定义窗口的处理逻辑
.aggregate(<window function>)
2. 非按键分区窗口(Non-Keyed Windows): 如果没有进行.keyBy()按键分区,原始的DataStream不会分成多个逻辑流,窗口操作将在一个任务上执行,即并行度为1。一般情况下,不推荐使用非按键分区窗口,因为它无法充分利用并行性。
在代码中,非按键分区窗口操作的调用方式如下:
stream.windowAll(<window assigner>)
- 直接使用.windowAll()定义窗口,传入一个窗口分配器,该分配器指定了窗口的类型和属性。
- 需要注意的是,对于非按键分区窗口操作,手动调整窗口算子的并行度是无效的,因为windowAll本身是非并行操作。
总之,窗口操作需要先确定是按键分区窗口还是非按键分区窗口,然后根据需求使用相应的API调用来定义窗口分配器和窗口函数,以实现窗口计算。通常情况下,按键分区窗口更常见和有用,因为它能够更好地利用并行性。
四、窗口分配器(Window Assigners)
|flink-1.9 docs:Apache Flink 1.9 Documentation: Windows
窗口分配器实际上是用于确定窗口类型的工具之一。
通常,最常见的定义方法是使用.window()方法,该方法需要传入一个WindowAssigner作为参数,并返回一个WindowedStream对象。
对于非按键分区窗口,可以直接使用.windowAll()方法,同样需要传入一个WindowAssigner,返回的是AllWindowedStream。
1、时间窗口
时间窗口是最常用的窗口类型,又可以细分为滚动、滑动和会话三种
1.1 滚动处理时间窗口
- 使用TumblingProcessingTimeWindows.of()来定义,需要指定窗口的大小。
- 例如:
stream.keyBy(...) .window(TumblingProcessingTimeWindows.of(Time.seconds(5))) .aggregate(...);
使用场景:适用于周期性统计,比如每5秒钟计算一次统计数据。
1.2 滑动处理时间窗口
- 使用SlidingProcessingTimeWindows.of()来定义,需要指定窗口的大小和滑动步长。
- 例如:
stream.keyBy(...) .window(SlidingProcessingTimeWindows.of(Time.seconds(10), Time.seconds(5))) .aggregate(...);
使用场景:适用于连续统计,可以用于监控系统中的滚动平均值计算。
1.3处理时间会话窗口
- 使用ProcessingTimeSessionWindows.withGap()或者.withDynamicGap()来定义,需要指定会话的超时时间或者动态提取会话超时时间的逻辑。
- 例如:
stream.keyBy(...) .window(ProcessingTimeSessionWindows.withGap(Time.seconds(10))) .aggregate(...);
使用场景:用于处理连续的会话数据,例如用户在应用中的活动会话,会话之间的间隔超过一定时间会被视为不同的会话。
stream.keyBy(...)
.window(ProcessingTimeSessionWindows.withDynamicGap(new SessionWindowTimeGapExtractor<Tuple2<String, Long>>() {
@Override
public long extract(Tuple2<String, Long> element) {
// 提取session gap值返回, 单位毫秒
return element.f0.length() * 1000;
}
}))
.aggregate(...);
1.4 滚动事件时间窗口
- 类似于滚动处理时间窗口,使用TumblingEventTimeWindows.of()来定义。
- 例如:
stream.keyBy(...) .window(TumblingEventTimeWindows.of(Time.seconds(5))) .aggregate(...);
场景:适用于处理数据流中的事件,例如实时日志分析、事件时间的流式处理等。
1.5滑动事件时间窗口
- 类似于滑动处理时间窗口,使用SlidingEventTimeWindows.of()来定义。
- 例如:
stream.keyBy(...) .windo