Windows窗口计算就是通过按照固定时间将数据切分成不同的窗口,然后对窗口内的数据进行计算。Flink DataStream API 将窗口抽象成独立的operator,并且在Flink DataStream内已经建了大多数窗口算子。每个算子中包含了Windows Assigner、Window Trigger(窗口触发器)、Evictor(数据剔除器)、Lateness(时延设定)、Output Tag (输出标签)、Windows Function等部分。
1、Windows Assigner
(1)Keyed和Non-Keyed窗口
在运行窗口计算时会根据上游数据的类型,设置不同的Windows Assigner。如果上游数据集的数据类型是KeyedStream,则调用DataStream API 的window()方法指定windows assigner;如果不是,则调用DataStream API 的windowAll()方法指定windows assigner。
(2)Windows Assigner
Flink支持两种类型的窗口,一种是基于时间的窗口,窗口基于起始时间戳和终止时间戳来决定窗口的大小。另一种是基于数量的窗口,根据固定的数据定义窗口的大小。
windows 窗口有四种类型:滚动窗口(Tumbling Windows)、滑动窗口(Sliding Windows)、会话窗口(Ssession Windows)和全局窗口(Global Windows)。
- 滚动窗口
滚动窗口是根据固定的时间或大小进行切割,且窗口和窗口之间的元素互不重叠。DataStream API 基于Event Time 和 Process Time 两种时间类型的Tumbling 窗口,对应的Assigner分别为TumblingEventTime和TumblingProcessTime。
- 滑动窗口
滑动窗口是在滚动窗口基础之上增加来窗口的滑动时间(Slide Time),且允许窗口数据发生重叠。DataStream API 基于Event Time 和 Process Time 两种时间类型的Tumbling 窗口,对应的Assigner分别为SlidingEventTime和SlidingProcessTime。
- 会话窗口
会话窗口是将某段时间内活跃度较高的数据聚合成一个窗口进行计算,窗口的触发条件是Session Gap,在规定的时间内如果没有数据活跃接入,则认为窗口结束,触发窗口计算结果。
Session Windows 窗口类型比较适合非连续型数据处理或周期性产生数据的场景。
//通过使用EventTimeSessionWindows 定义 EventTime 滚动窗口
window(EventTimeSessionWindows.withGap(Time.milliseconds(10)))
//通过使用ProcessTimeSessionWindows 定义 ProcessTime 滚动窗口
window(ProcessTimeSessionWindows.withGap(Time.milliseconds(10)))
- 全局窗口
全局窗口就是将所有相同的key的数据分配到单个窗口中进行计算,窗口没有起始和结束时间,窗口需要借助Trigger来触发计算,如果对Global Windows 指定Trigger ,窗口是不会触发计算。
//通过GlobalWindows 定义Global Windows
window(GlobalWindows.create())
2、Windows Function
Flink 中提供四种类型的Window Function,分别是ReduceFuntion、AggregateFunction、FoldFunction以及ProcessWindowFunction。
四种类型的Window Function 可以根据计算原理的不同可以分为两大类,一类是增量聚合函数,对应有ReduceFunction、AggregateFunction、FoldFunction;另一类是全量窗口函数,对应有ProcessWindowFunction。增量聚合函数计算性能比较高,占用存储空间少,主要因为基于中间状态的计算结果,窗口中只维护中间结果的状态值,不需要缓存原始数据。而全量窗口函数使用的代价相对比较高,性能比较弱,主要是因为此类算子需要对所有属于此类窗口的接入数据进行缓存,然后等到窗口触发的时候,才对所有原始数据进行汇总计算。
- ReduceFunction
ReduceFunction 定义了对输入的两个相同类型的数据元素按照指定的计算方法进行整合的逻辑,然后输出类型相同的一个结果元素。
//直接使用表达式
reduce{(v1,v2)=>(v1._1,v1._2+v2._2)}
//实现ReduceFunction接口
reduce(new ReduceFunction [(Int,Long)]) {
override def reduce(t1:(Int,Long),t2:(Int,Long)):(Int,Long) = {
(t1._1,t1._2+t2._2)
}
}
- AggregateFunction
和ReduceFunction 相似,AggregationFunction也是基于中间状态计算结果的增量计算函数,但AggregationFunction 在窗口上更加通用。AggregationFunction接口比ReduceFunction更加灵活,实现复杂度也相对较高。AggregateFunction接口中定义了三个需要重写的方法,add()定义数据的添加逻辑,getResult定义了根据accumulator计算结果的逻辑,merge定义合并accumulator的逻辑。
- FoldFunction
FoldFunction 定义了如何将窗口中的输入元素与外部的元素合并的逻辑。
- ProcessWindowFunction
更加灵活地支持基于窗口全部数据元素的结果计算
- Incremental Aggregation 和 ProcessWindowsFunction 整合
两者整合后在实现增量聚合计算的同时,也可以操作窗口的元数据信息以及状态数据。
- ProcessWindowFunction 状态操作
除了能够通过RichFunction 操作keyed state 之外,ProcessWindowFunction 也可以操作基于窗口之上的状态数据,这类状态被称为Pre-window State 。
博主的微信公众号,不定期发布技术文章,喜欢的可以关注一下哈