Flink如何用窗格来优化窗口

文章目录

对于处理时间类型的窗口(包含了翻滚窗口和滑动窗口),Flink会使用称之为”pane”的技术来优化这类时间窗口的计算。

pane:中文译为窗格。它将窗口划分成多个规则的部分,这些部分可看作子窗口,可简单理解为对窗口再次分片。

当前很多系统对滑动窗口进行查询(计算)的通用解决方案是缓存每个输入的元素,直到其不出现在所有的窗口中(随着时间的推移、窗口的滑动,元素对应的时间点将超出窗口的时间范围)。既然每一个输入元素都会同时属于多个窗口,那么这种方案会缓存一个元素直到它所归属的最后一个窗口被计算为止。

这种方案存在两个主要的问题。首先,它要求缓冲区的大小是无界的:在任何一个时间点,当前窗口的所有元素都必须存在于缓冲区中,因此缓冲区的大小就取决于窗口的大小以及数据到达的速率。其次,对每个输入元素处理多次会导致很高昂的计算开销。比如,对于下面这个滑动窗口示例,每个元素都会被处理四次(参考如下示意图,假设对窗口大小为4分钟,滑动步长为1分钟的滑动窗口进行计算,则图中每个正方形格子代表滑动步长的时间大小,以图中深色格子为例,随着窗口的滑动其中的元素都会被计算四次)。随着窗口大小和滑动步长比例的增大,每个元素被处理的次数也随之增大。考虑到流处理系统中庞大的数据量以及快速的数据到达速率,减少大量的缓冲区空间需求以及计算次数就显得尤为重要。

window-and-pane

而基于窗格的计算优化是解决上述两个问题的有效方式。窗格通过对窗口进行子聚合来减少对缓冲区的空间需求,通过在计算窗口时共享子聚合的结果来减少计算次数。我们仍然以上图作为示例分析窗格机制,上图中窗口流被分割为一分钟的窗格(这里窗格大小等于滑动步长并不是特意为之,窗格如果能被设置为滑动步长是最好的选择,但有时并不一定能实现,具体窗格大小如何计算,后面我们会提及),每一个四分钟大小的窗口都由四个连续的窗格组成。W3窗口由窗格P3-P6组成,每一个窗格都是四个窗口的组成部分,例如P5是窗口W2-W5的组成部分。为了完成对窗口的计算,我们先对窗格进行子聚合,而对窗口的聚合则建立在对四个窗格的聚合结果之上。

Flink对窗格的应用有其限制的场景。首先,必须是基于键分组过的窗口流,也就是窗口必须是基于键的;其次,Flink只支持具备定时触发器的处理时间(这一点也将计数窗口等非时间窗口排除在外);第三,凡是配备了驱逐器的窗口都无法应用基于窗格的计算优化。

虽然窗格发挥优势的对象是处理时间的滑动窗口,但因为翻滚窗口可以看作滑动窗口的特例(滑动步长即为窗口大小),所以基于窗格的计算优化也同样适用于处理时间的翻滚窗口。

下面我们来看Flink对窗格的实现,在Flink中支持两种类型的窗格:聚合窗格和累加窗格。类图如下所示:

TimePanes-class-diagram

AbstractKeyedTimePanes为两种类型的窗格提供模板,它维护了最新的窗格引用,并将之前的窗格都存储到一个队列里,队列中始终维护着固定数量的窗格个数(个数为:窗口的大小除以窗格的大小)。在Flink中存储窗格信息的数据结构是KeyMap(它是对哈希表的一种特殊实现,这种实现的特点是能够更高效地遍历元素),并提供了traverseMaps方法用于对KeyMap集合进行遍历,定义被遍历元素的处理逻辑需要实现接口:TraversalEvaluator。

AbstractKeyedTimePanes定义了两个抽象方法,供子类实现:

  • addElementToLatestPane:将元素添加到最新的窗格中;
  • evaluateWindow:对窗口进行计算(最终依赖于各自对TraversalEvaluator接口的实现);

AggregatingKeyedTimePanes和AccumulatingKeyedTimePanes是抽象类AbstractKeyedTimePanes的两个派生类。AggregatingKeyedTimePanes类采用的计算函数是ReduceFunction,而AccumulatingKeyedTimePanes类采用的计算函数则是一般的WindowFunction。

这两个派生类对上面两个抽象方法的实现有很大的差别:AggregatingKeyedTimePanes是在addElementToLatestPane方法中即时计算窗格的值并更新,在evaluateWindow方法中其实现的AggregatingTraversal也是即时求值的。而AccumulatingKeyedTimePanes正好与之相反,其addElementToLatestPane只是将元素加入到集合中,对应的WindowFunctionTraversal则是惰性求值(先收集元素,到最后再应用函数进行计算)。

窗格是可以“滑动”的(对应方法:slidePanes),对应到代码实现上:就是将“当前”窗格加入到双端队列中,然后重新生成一个新的窗格对象作为“当前”窗格。


微信扫码关注公众号:Apache_Flink

apache_flink_weichat


QQ扫码关注QQ群:Apache Flink学习交流群(123414680)

qrcode_for_apache_flink_qq_group