前言
研究了一阵子Flink Streaming,将学习的东西记录下来,今天写一下WaterMark
在写WaterMark之前,先来了解一下时间窗口
时间窗口
在流式计算中,数据像流水一样源源不断地来,数据是无边界的,当需要操作(计算)数据时,在茫茫数据流中不知从何开始,所以,这时我们需要一个边界(范围)将整个数据流划分开来
于是,我们将源源不断的数据流划分为一段一段的数据集,称之为窗口
流式系统的窗口有多种,最基本也是最常见的是时间窗口,顾名思义,根据时间来划分出一个一个有范围的窗口
关于时间的划分,通常来说,有两种:
1.Event Time 数据发生的时间
2.Process Time 数据处理的时间
这两种时间的选择,根据需求有所不同,一般而言会选择Event Time,当然,并不是所有的场景,都会选择Event Time
这里有张图,表示了Event Time与Process Time的实际效果图,提供了一些选择参考
图中黑色虚线表示:在理想的状况下,Event Time和Process Time总是想等的,数据发生,立即处理,没有延迟
事与愿违,现实中由于各种各样的问题,例如:网络波动、设备故障、软件异常、天灾人祸等,就如图中红色线表示,Process Time总会与Event Time有一些延迟
如果应用场景考虑到时间准确性,因为Process Time会有时间延迟,所以Process Time不是一个明智的选择
当选择Event Time时,也会因为各种各样的问题,例如:网络、背压,造成数据乱序
乱序的数据如何划分窗口呢?
在不同的系统中,为了解决该问题,会有不同的解决方法,在Flink中,使用了WaterMark(下面会介绍)
介绍过常见的时间窗口后,了解一下在Flink中时间窗口的种类
Flink中时间窗口的种类
Flink中窗口种类有3种,包含常见的Process Time与Event Time外,还有一个Ingestion Time:是指数据进入flink的时间,在source处插入的时间,无法处理乱序事件
一般情况下,Event Time时间窗口是我们的选择,Flink为了处理Event Time乱序,借鉴了Google的Mill Wheel项目,引入了WaterMark来解决数据乱序问题
WaterMark
先来举个例子:
需要统计8:00:00-8:00:10之间的Word Count,也就是对Event Time在8:00:00-8:00:10的数据进行Word Count,Event Time可能会发生延迟或乱序,Flink如何去判断在8:00:00-8:00:10之间,数据都已经到达,可以进行计算了呢?
这时就需要WaterMark,WaterMark是一种特殊的record,其本质是一个时间戳,它单调递增,它也会随着数据流转到接下来的算子,会被Flink识别和处理
将WaterMark的时间戳视作一个标准(让Flink知道已经处理到什么位置),按照这个标准去处理数据,后续流入数据的Event Time(Flink中需要显式的指定Event Time)都要大于这个标准,Flink才会处理,如果迟来的数据Event Time小于这个标准,那么就被视为迟到数据,Flink则不会处理,迟到数据Flink也有一些机制去处理
回到上面的例子,当Flink算子收到了一条带有8:00:11的WaterMark的数据,那么Flink就会认为,8:00:11之前的数据都已经到达了,可以进行计算
一切看似完美,实则不然,如果Flink处理了8:00:11的WaterMark后,由于网络延迟,突然来了一条8:00:05的数据该如何处理?如果数据不重要,不处理也行,如果数据很重要,这时就需要调整生产WaterMark的设定了
WaterMark的设定
由于种种原因,造成数据的乱序与延迟,在设置WaterMark时可以允许一定时间段的延迟(当然也不可能无限的等待),且在触发下一个窗口计算前,也会将Event Time进行排序,以保证数据有序
WaterMark设定方法有两种:
1.Punctuated Watermark : 数据流中每一个递增的EventTime都会产生一个Watermark
在实际的生产中Punctuated方式在TPS很高的场景下会产生大量的Watermark在一定程度上对下游算子造成压力,所以只有在实时性要求非常高的场景才会选择Punctuated的方式进行Watermark的生成
2.Periodic Watermark : 周期性的(允许一定时间间隔或者达到一定的记录条数)产生一个Watermark
在实际的生产中Periodic的方式必须结合时间和积累条数两个维度继续周期性产生Watermark,否则在极端情况下会有很大的延时
两种方式根据场景进行选择
算子对WaterMark的处理
WaterMark是可以被算子处理,算子内部会有个时间记录器,记录了各个Window的结束时间
当算子收到一个WaterMark时,算子会根据这个WaterMark的时间戳更新内部的Event Time Clock,当前记录的时间与WaterMark进行对比,如果WaterMark大于记录的时间,则更新该记录为当前的WaterMark值
并行多流的Watermark处理
在实际的流计算一个job中可能会处理多个数据源的数据,对数据源的数据进行分组,那么来自不同数据源的相同key值会shuffle到同一个处理节点,并携带各自的WaterMark,Flink要保证WaterMark要保持单调递增,多个数据源的WaterMark汇聚到一起时候可能不是单调自增的,为了解决这个问题,在Flink的一些算子使用多个数据源时,例如union、keyby,算子同时会收到各自数量携带的WaterMark,选取其中最小的那个WaterMark,并广播出去
至此,整个WaterMark部分介绍完毕了,下篇文章通过代码的方式来演示如何分配WaterMark和其他功能
最后,我画了一张图,便于理解WaterMark