流式计算系统系列(3):窗口
在上一篇文章中我们提到流式计算系统当中聚合数据流以挖掘更多信息的例子,分别是【网站每隔一个小时的访问量】【每隔五分钟输出近一个小时成交额高的商品】和【实时显示用户的访问热点】。我们在上一篇文章中以此引出了流式计算系统当中的时间的概念,而这几个例子本身是基于时间的窗口的例子。
窗口(Window)暂存了上游输入的部分数据,以用于在给定的触发条件下对暂存的这部分数据进行聚合产生输出到下游的结果。可以看到,在这个定义下,窗口主要的属性包括:
- 窗口暂存数据的逻辑
- 窗口触发计算的逻辑
我们将从这两个属性入手,介绍窗口的分类及特定分类的语义和实现手法
窗口暂存数据的逻辑
这一部分的内容即确定一个窗口内暂存了哪些数据,这个问题包括了两个部分,即新到来的数据暂存到哪个窗口中,以及此前暂存的数据何时清理。
我们看到,在上面分拆问题的描述中,我们反转了行为的主体和受体,即不是由窗口来决定选择哪些数据,而是由数据来决定其归属的窗口。这样的逻辑更加符合流式计算中数据驱动的处理方式。在 Flink 的实现中,这个行为由 WindowAssigner
来负责,给定输入数据,由它判断将这个数据归属到若干个窗口当中。
我们看到 Flink 当中数据归属到窗口这个行为的分类。
GlobalWindow
全局所有数据都归属到同一个窗口。这是一个语义上正确的窗口,不过显而易见的是将所有数据都归属到同一个窗口的价值是有限的,同时面临着暂存区溢出的问题。通常这种窗口会结合定制的清理逻辑(Evictor)和触发逻辑(Trigger)来对窗口中的数据进行定制化的聚合和清理。
TimeWindow
数据按照附带的时间戳归属到不同的时间窗口当中。不同的数据几乎不会完全落到同一个窗口当中,因此时间窗口本身就具有将数据按照其时间戳初步划分批次的作用。我们看到具体的时间窗口的例子来介绍,这里的时间窗口按照其时间属性分为 Processing Time 的时间窗口或 Event Time 的时间窗口,关于不同时间属性的区别在前一篇文章中已经介绍,在此就不再做出区分。
TumblingTimeWindow
此窗口中文对应称为滚动窗口,我们首先看到一个滚动窗口的典型动图。
可以看到,滚动窗口根据划定的长度彼此紧邻而不交叉的出现,对于一个到来的数据,根据时间属性取得其时间戳,即可以算出它所对应的时间窗口。关于这一点,我们利用 Flink 的源码来做直观的解释。
// From TimeWindow.java
public static long getWindowStartWithOffset(long timestamp, long offset, long windowSize) {
return timestamp - (timestamp - offset + windowSize) % windowSize;
}
相关文章