浅谈WaterMark

一直在找理由,没有时间空下来总结一下flink相关的知识点,终于下了决心:后续专注了flink的专题总结。想了想还是以watermark开始,本文只是谈谈个人对待watermark的理解,如有哪里说得不恰当,欢迎讨论。起初对Flink的watermark感动一点困惑,经过时间的沉淀,源码断断续续的阅读,稍微清楚一点,下面我将从一些概念说起。

1、时间属性

Flink官方中有三种时间类型,Event Time(事件时间)、Processing Time(处理时间)和Ingestion Time(摄入时间),从字面意义上很容易理解其中一个大概,这里再陈述一下:

事件时间:一般来说,我们提供的事件时间通常是数据的原始创建时间,代表是事件发的时间,事件时间一定在数据的架构中,为数据的某一列。

处理时间:系统对事件进行处理的本地系统时间。

摄入时间:事件进入弗林克系统的时间其在概念上位于事件时间和处理蒂姆之间,在内部,摄入时间和事件时间非常相似,但具有自动时间戳分配和自动水印生成功能。

谈这三个时间主要是为了引出watemark,因为很多场景下,事件发生的时间事件时间是我们业务所关心的,基于事件时间计算,采用某种策略,则无论是采用实时流数据还是历史数据,都可以保证结果是一致的为了更生动的描述事件时间和事件流进系统(这里指Flink)的关系,特画了下面的图做解释:其中数字代表着某个事件的发生时间,可以看出事件实际到达的顺序和其发生的时间不一致。

针对实时流计算,一般的处理方式是来一个元素处理一个元素,这样才能实时。但是针对基于Event Time的一些应用,我们要求处理的准确性,必须缓存,因为第一个事件比如5到达时,不知道后面来的事件发生的时间比当前的事件早,因此必须要等到至少第二个事件到达才能确定是否输出第一个事件的计算结果,这样就会造成延迟。

但是在第二个事件3到达后,是否还有事件比事件3发生的事件更早呢,是否继续缓存等待下去?如果等待下去,等待多久呢?因此必须要有个机制策略保证不再等待,触发当前缓存的数据计算并输出。

         那么,当前的计算已经计算并输出,如果再较早发生的事件晚到达了,怎么处理?如上图,假如在在事件9时我们触发了计算并输出了结果,可是下一个事件8到达了。我们想到了两种处理策略:1,把事件8加进上次缓存数据中重新计算输出; 2,丢弃不计算第二种策略丢弃不计算好处理,第一种策略需要上次的缓存数据,这里又会面临一个两个问题:1,上次缓存数据计算后不能清除缓存; 2,缓存要保留多久,因为如果一直保留缓存,势必造成增加整个系统的内存压力等。

带着这些问题,我们走进Flink。Flink的的watermark机制和lateness概念针对上述问题做了很好的全面解读。

注:本文主要讲述Flink使用EventTime的相关内容,flink默认的实时间属性是Processing Time,故需要通过Flink的接口env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)设置EvenTime时间特性。

2、水印的原理

Watermark是一种衡量Event Time进展的机制,它是数据本身的一个隐藏属性。通常一条记录中某个字段就代表了该记录的发生时间。例如基于Event Time的数据,自身都包含一个类型为timestamp的字段rowtime,例如1543903383(2018-12-04 14:03:03),定义一个基于rowtime列,策略为偏移3s的watermark,这条数据的水印时间戳则是:

1543903383-3000 = 1543900383(2018-12-04 14:03:00)

该条数据的水印时间含义:timestamp小于1543900383(2018-12-04 14:03:00)的数据,都已经到达了。

2.1、窗口触发条件

上面谈到了对数据乱序问题的处理机制是watermark+window,那么window什么时候该被触发呢?

基于Event Time的事件处理,Flink默认的事件触发条件为:

对于out-of-order及正常的数据而言

1)watermark的时间戳> = window endTime

2)window有事件发生。

2,对于late element太多的数据而言(设置了lateness选项,默认为0)

1)Event Time > watermark的时间戳

2.2、图解Watermark

对于lateness选项我们先不考虑,后面再再提及。

我们设置一个偏移为5秒的watermark策略,大小为10秒的窗口,为了能更好的理解watermark,我们作如下类比,数据发生的时间空间为A时间空间,watermark的时间空间为B时间空间,则B时间空间总比A时间空间晚5秒发生。

浅谈WaterMark_第1张图片

如上图,矩形小框代表窗口大小,大小为10秒,Flink默认会根据选择的时间(这里是Event Time)分配窗口。假设数据发生的时间rowtime从0开始,则预先分配的窗口即使[0,10),[10,20],[20,30],[30,40] ......

A时间轴上的时刻是一定的,同样B时间轴上的时刻也是一定的,B空间时间轴上的时刻相对A时刻轴上的时刻总是晚5秒。在同一时间坐标系S下,假设S时间坐标和A时间一样,则A时间轴上的时刻在S坐标系下时间值不变,但B时间轴上的时刻在S时间坐标系下时间值都变“大” 5s了。即在第一个窗口[0,10],如果一个记录中rowtime为10s的数据在S坐标系下9s到达了,但是其watemark其实是10-5 = 5s,还没有到达第一个窗口的end Time,故不会触发窗口计算;如果一个记录中rowtime为8s的数据在S坐标系下12s到达了,但其watermark其实是8-5=3s小于之前的watermark,故此时不更新watermark(一般情况下),watermark的时间戳仍然是5秒,也没有达到第一个窗口的触发条件;如果一个记录中rowtime为12s的数据在S坐标系下13s到达了,其watemark其实是12-5 = 7 > 5,更新watermark的时间戳为7秒,但是也没有达到一个窗口的触发条件;如果一个记录中rowtime为15s的数据到达了,其watemark其实是15 -5 = 10s,达到了触发条件 ,大于window endTime,故窗口此时触发计算,如果后面再有rowtime<10s的数据到达,将会被丢弃(没有设置latness选项)。

这样看是不是感觉计算”延迟了5秒”,确实,计算延迟了,但是计算的延迟是针对设置的时间属性延迟的,这里是EVENTTIME,和系统时间没有关系。

2.3、Late Elements

某些元素有可能在watermark(t)发生之后,也会出现更多的时间戳t'<= t的元素。上文我们提到,默认情况下,当watermark> = Window EndTime后,这些晚到的元素将会被丢弃。但是现实业务处理中,我们又不希望丢弃这些元素,如果设置的watermark太大,数据积压又会导致系统性能下降。考虑到这一点,Flink允许为窗口指定一个最大延迟时间,这个最大延迟时间即是窗口触发计算后允许多长时间窗口的数据才能被删除,默认值为0。即当该窗口触发计算后,在最大延迟时间内,再有属于该窗口内的元素到达将会重新触发计算。

假设Flink设置的watermark允许延迟的策略为t1秒,设置的late Elements的lateness值为t2秒,窗口首次触发的的系统时间为t(假设已经转化为秒),则这些late Elements到达的系统时间如果在[t, t+t2)时间内,将会再次触发计算。

你可能感兴趣的:(Flink)