在上一篇中,介绍了Flink里时间的概念和窗口计算,在实际生产过程中,由于网络等原因,许多数据会延迟到达窗口,这种情况Flink如何处理?Watermark登场,本文从这几点进行介绍:水印的概念、水印如何计算、允许延迟和侧道输出、水印生成策略、案例及代码。
1、一个小例子
讲解概念前,我先举个例子。比如工厂的生产线有一批货物要发出,每个货物上都有一个生产时间的标记,司机在门口等待货物,他每天9:00出发,只要他看到最新过来的货物上的时间是9:00,那他立马就出发。
但是久而久之他发现,有些货物会延迟到达,比如9:00的货物已经到达,忽然他又看到一个8:59的货物到达了,为了能够一次性运送更多的货物,他决定继续多等5分钟,即:如果9:05的货物到达后,他就立马出发,不再等待了。
这样的话,即使有延迟到达的货物,只要它们能在9:05分之前到达,那这部分货物也会被发出。
2、水印的概念
我们来思考一个场景,比如,对于窗口[12:00-12:10),事件时间为12:04的数据,由于网络原因,到达Flink的时间是12:11。此时窗口已经关闭了,该数据将不属于任何窗口,最终这个数据会丢失。
所以,为了保证计算结果的正确性,需要让窗口等待延迟数据到达后再进行计算,但是也不能无限期地等待下去,必须有一种机制来确定何时触发窗口计算,这种机制就是水印(Watermark)。
水印是一种用于衡量事件时间进度的机制,其表示某个时刻(事件时间)以前的数据将不再产生,因此水印指的是一个时间点。水印作为数据流的一部分流动,并带有时间戳t。t表示该流中不应再有时间戳小于等于t的元素(即时间戳早于或等于水印的事件)。
如下图,显示了带有时间戳和嵌入式水印的事件流,事件是按顺序排列的,这意味着水印只是流中的周期性标记。
水印对于乱序流至关重要,如下图,其中事件不是按其时间戳排序的。通常,水印是数据流中一个点的声明,表示水印之前的所有事件都应该到达。一旦水印到达,算子则认为某个时间周期内的所有事件已经被收到,不会再有更多符合条件的事件了。
3、水印如何计算
计算水印需要提前指定一个允许最大延迟时间的参数。
水印 = 进入Flink的当前最大事件时间(比如上面例子中的9:05分到达的货物) ‒ 允许最大延迟时间(比如上面例子中的司机多等待的5分钟)。
当水印 >= 窗口结束时间时,立即触发窗口计算,计算完毕后发射出计算结果并销毁窗口,否则窗口将一直等待。
所以,窗口触发计算的规则是:进入Flink的当前最大事件时间 >= 窗口结束时间+允许最大延迟时间。可见,设置水印后会改变窗口的触发计算规则。
例子:假