一、概念
将要计算的数据限制一个范围,统计一个范围内的数据。将无界的数据切割成有届的数据
二、分类
1)按照驱动类型分
(1):时间类型窗口(Time Window)
以时间点来定义窗口的开始(start time)和结束(end time),截取出来的就是某一时间段的数据
(2):计数窗口(Count Window)
基于元素的个数来截取数据,到达固定数据就触发计算并关闭窗口
2)按窗口分配数据的规则分类
可以划分为四类:滚动窗口、滑动窗口、会话窗口和全局窗口
(1):滚动窗口
有固定的大小,是对数据"均匀分片"。**窗口之间没有重叠,也不会有间隔。**每个数据都会被分配到一个窗口,而且只属于一个窗口。既可以基于时间,又可以基于个数。(滑动步长等于窗口大小,可以理解成是一种特殊的滑动窗口)
(2):滑动窗口
窗口大小是固定的,但是窗口之间并不是首尾相接的,而是可以"错开"一定的位置。定义滑动窗口要有两个参数:窗口大小和滑动步长。既可以基于时间,又可以基于个数。
(3):会话窗口
基于"会话"对数据分组。只能基于时间来定义。最重要的参数就是**会话的超时时间,会话窗口的长度不固定,起始时间和结束时间也不确定**
** (4):全局窗口**
** **会把相同key的所有数据都分配到一个窗口中。这种窗口没有结束的时候,默认是不会触发计算的。需要自己定义触发器
三、窗口 API
1) 按键分区(Keyed)和非按键分区(Non-Keyed)
按键分区的数据流KeyedStream,没有按键分区的数据流DataStream,要观察是否有keyby操作
(1) 按键分区 keyed window
经过keyBy划分后,数据会被分成多条逻辑流,就是KeyedStream。基于KeyedStream进行窗口操作时,窗口计算会在多个并行子任务上同时执行。相同key的数据被发往同一个并行子任务。代码实现上,需要先对DataStream调用.keyBy()按键分区,然后再调用.window()定义窗口
(2)非按键分区
如果没有进行keyby,那么原始的DataStream就不会分成多条逻辑流,这时候的窗口逻辑只能在一个task上执行,相当于并行度变成了1。代码中直接基于DataStream调用.windowAll()定义窗
2) 代码中窗口API的调用
窗口操作主要有两个部分:窗口分配器(Window Assigners)和窗口函数(Window Functions)。
四、窗口分配器
1)作用
定义数据应该被“分配”到哪个窗口。可以说,窗口分配器其实就是在指定窗口的类型
2)方法
通用的定义方式为.window()调用,穿入一个WindowAssigner作为参数,返回一个WindowedStream。如果时非按键分区的,直接调用WindowAll()方法,同样传入一个WindowAssigner,返回AllWindowedStream
3)实现
3.1 时间窗口
3.1.1 滚动处理时间窗口
窗口分配器由TumblingProcessingTimeWindows提供,调用他的静态方法.of(),传入一个Time类型的参数size,表示滚动窗口的大小
3.1.2 滑动处理时间窗口
窗口分配器由SlidingProcessingTimeWindows提供,调用他的静态方法.of(),传入两个Time类型的参数,size和slide,也就是窗口的大小和滑动的步长
3.1.3 处理时间会话窗口
窗口分配器由ProcessingTimeSessionWindow提供,需要调用他的静态方法.withGap,或者.withDynamicGap()。需要传入一个Time类型的参数size,表示会话的超时时间
3.1.4 滚动事件时间窗口
窗口分配器由类TumblingEventTimeWindows提供,和滚动处理时间窗口完全一致
3.1.5 滑动事件事件窗口
窗口分配器由SlidingEventTimeWindows提供,用法和滑动处理时间完全一致
3.1.6 事件时间会话窗口
窗口分配器由类EventTimeSessionWindows提供,用法和处理时间会话窗口完全一致
3.2 计数窗口
3.2.1 滚动计数窗口
直接调用.countWindow,传入一个长整型的参数size,表示窗口的大小
3.2.2 滑动计数窗口
直接调用.countWindow,传入两个参数,表示窗口大小和步长
3.3.3 全局窗口
是计数窗口的底层实现,一般需要在自定义窗口时使用。他的定义是直接调用window,窗口分配器由GlobalWindows提供
五、 窗口函数
1)增量聚合
来一条计算一条,但是并不会立即输出,当窗口触发才会输出
①:reduce
特点 1、相同key的第一条数据来的时候,不会调用reduce方法
2、来一条数据就会计算一次,但是不会输出
3、 窗口触发的时候,才会输出窗口的最终计算结果
②:aggregate
增量聚合是有三中类型的,输入类型,累加器(中间结果的类型),输出类型。aggregate可以定义三种类型,而reduce的这三种类型都为相同的,不够灵活
2)全窗口函数
数据来了不计算,存起来,窗口触发的时候,计算并输出结果。另外,全窗口函数提供了上下文,可以拿到比如窗口的开始时间、结束时间等信息。老版本用apply方法,新版本使用process方法,new ProcessWindowFunction<输入类型,输出类型,key类型,窗口类型>
3) 增量+全窗口
优点:既实现了增量来一条计算一条,又实现了可以获得上下文更多信息。
实现方式:增量聚合算子中传入两个参数,比如aggregate,传入AggregateFunction和ProcessWindowFunction。aggregate计算完的结果传入到ProcessWindowFunction,只有一条结果
六、其他API
1)触发器(Trigger)
主要用来控制窗口什么时候触发计算。基于.window之上嗲用.trigger方法,可以传入一个自定义的窗口触发器。会通过Trigger.FIRE方法进行触发
2)移除器 (Evictor)
主要用来定义移除某些数据的逻辑。基于WindowedStream调用.evictor()方法,可以传入一个自定义的移除器。Evictor是一个接口,不同的窗口类型都有各自预实现的移除器
七、窗口的划分及生命周期(查看源码)
1. 点击窗口分配器进入源码
2. 找到assignWindows方法
进入getStaggerOffset方法,查看计算start规则
1) start
remainder计算方法:
例如当前时间为13s,窗口大小为10s,其中offset默认是0,用当前13 % 10 = 3,remainder>0走else路线,返回13 - 3 = 10。
总结:start最终就要取窗口长度的整数倍。eg:10s -> 20s、30s...
- end
end = start + 窗口长度(窗口时左闭又开的)
属于本窗口的 最大时间戳是end - 1ms(源码中定义的)
3)生命周期
创建:属于本窗口范围的第一条数据来的时候,现new的(如上图new TimeWindow)
思考:那是否再来这个区间的数据还会new呢?
当然不会,使用的singletonList单例集合
销毁:关窗, 时间的进展 >= 窗口的最大时间戳(end - 1ms) + 允许迟到的时间(默认为0 )
版权归原作者 大数据领军人 所有, 如有侵权,请联系我们删除。