Window(窗口计算)
窗口计算是流计算的核心,通过使用窗口对无限的流数据划分成固定大小的 buckets,然后基于落入同一个bucket(窗口)中的元素执行计算。Flink将窗口计算分为两大类。
一类基于keyed-stream窗口计算。
stream
.keyBy(...) <- 分组
.window(...) <- 必须: "assigner" 窗口分配器
[.trigger(...)] <- 可选: "trigger" 每一种类型的窗口系统都有默认触发器
[.evictor(...)] <- 可选: "evictor" 可以剔除窗口中元素
[.allowedLateness(...)] <- 可选: "lateness" 可以处理迟到数据
[.sideOutputLateData(...)] <- 可选: "output tag" 可以Side Out获取迟到的元素
.reduce/aggregate/fold/apply() <- 必须: "function"
[.getSideOutput(...)] <- 可选: 获取Sideout数据 例如迟到数据
直接对non-keyed Stream窗口计算
stream
.windowAll(...) <- required: "assigner"
[.trigger(...)] <- optional: "trigger" (else default trigger)
[.evictor(...)] <- optional: "evictor" (else no evictor)
[.allowedLateness(...)] <- optional: "lateness" (else zero)
[.sideOutputLateData(...)] <- optional: "output tag" (else no side output for late data)
.reduce/aggregate/fold/apply() <- required: "function"
[.getSideOutput(...)] <- optional: "output tag"
Window Lifecycle
简而言之,一旦应属于该窗口的第一个元素到达,就会创建一个窗口,并且当时间|WaterMarker(Event Tme或Process Time)超过其Window End 时间加上用户指定的允许延迟时,该窗口将被完全删除。窗口触发计算前提 水位线 没过窗口的End Time这个时候窗口处于Ready状态,这个时候Flink才会对窗口做真正的输出计算。
Trigger:负责监控窗口,只有满足触发器的条件,窗口才会触发。(例如 水位线计算)
evictor: 在窗口触发之后在应用聚合函数之前或之后剔除窗口中的元素。
Window Assigners
Window Assigners定义了如何将元素分配给窗口。在定义完窗口之后,用户可以使用reduce/aggregate/folder/apply等算子实现对窗口的聚合计算。
- Tumbling Windows :滚动,窗口长度和滑动间隔相等,窗口之间没有重叠。(时间)
dataStream.flatMap(_.split("\\s+"))
.map((_,1))
.keyBy(0)
.window(TumblingProcessingTimeWindows.of(Time.seconds(5)))
.reduce((v1,v2)=>(v1._1,v1._2+v2._2))
.print()
- Sliding Windows:滑动,窗口长度 大于 滑动间隔,窗口之间存在数据重叠。(时间)
dataStream.flatMap(_.split("\\s+"))
.map((_,1))
.keyBy(0)
.window(SlidingProcessingTimeWindows.of(Time.seconds(4),Time.seconds(2)))
.fold(("",0))((z,v)=>(v._1,z._2+v._2))
.print()
- Session Windows: 会话窗口,窗口没有固定大小,每个元素都会形成一个新窗口,如果窗口的间隔小于指定时间,这些窗口会进行合并。(时间)
dataStream.flatMap(_.split("\\s+"))
.map((_,1))
.keyBy(0)
.window(ProcessingTimeSessionWindows.withGap(Time.seconds(5)))
.aggregate(new AggregateFunction[(String,Int),(String,Int),(String,Int)] {
override def createAccumulator(): (String, Int) = {
("",0)
}
override def add(value: (String, Int), accumulator: (String, Int)): (String, Int) = {
(value._1,value._2+accumulator._2)
}
override def getResult(accumulator: (String, Int)): (String, Int) &