文章目录
1. Flink 中的时间和窗口
1.1 时间语义
在flink中,当希望对数据按照时间窗口来进行收集计算时,时间的衡量标准就非常重要

如图:在事件发生之后,生成的数据被收集起来,首先进入分布式消息队列,然后被 Flink 系统中的 Source 算子读取消费,进而向下游的转换算子(窗口算子)传递,最终由窗口算子进行计算处理。
在这个过程中,有两个非常重要的时间点:一个是数据产生的时间,把它叫作事件时间(Event Time);另一个是数据真正被处理的时刻,叫作处理时间(Processing Time);窗口操作以那种时间作为衡量标准,就是所谓的时间语义(Notions of Time)。由于分布式系统中网络传输的延迟和时钟漂移,处理时间相对事件发生的时间会有所滞后
注:
在事件时间语义下,对于时间的衡量,不再依赖于任何机器的系统时间,而是依赖于数据本身(Timestamp作为一个属性嵌入到数据中)。简单来说,这相当于任务处理的时候自己本身是没有时钟的,所以只好来一个数据就问一下“现在几点了”;而数据本身也没有表,只有一个自带的“出厂时间”,于是任务就基于这个时间来确定自己的时钟。由于流处理中数据是源源不断产生的,一般来说,先产生的数据也会先被处理,所以当任务不停地接到数据时,它们的时间戳也基本上是不断增长的,就可以代表时间的推进。
当然这里有个前提,就是先产生的数据先被处理,这要求保证数据到达的顺序。但是由于分布式系统中网络传输延迟的不确定性,实际应用中数据流往往是乱序的。在这种情况下,就不能简单地把数据自带的时间戳当作时钟了,而需要用另外的标志来表示事件时间进展,在 Flink 中把它叫作事件时间的水位线(Watermarks)。
1.2 两种时间语义的对比
在分布式环境中,处理时间其实是不确定的,各个并行任务时钟不统一;而且由于网络延迟,导致数据到达各个算子任务的时间有快有慢,对于窗口操作就可能收集不到正确的数据了,数据处理的顺序也会被打乱。这就会影响到计算结果的正确性。所以处理时间语义,一般用在对实时性要求极高、而对计算准确性要求不太高的场景。
而在事件时间语义下,水位线成为了时钟,可以统一控制时间的进度。这就保证将数据划分到正确的窗口中,比如 8 点 59 分 59 秒产生的数据,无论网络传输的延迟是多少,它永远属于 8 点~9 点的窗口,不会错分。但数据还可能是乱序的,要想让窗口正确地收集到所有数据,就必须等这些错乱的数据都到齐,这就需要一定的等待时间。所以整体上看,事件时间语义是以一定延迟为代价,换来了处理结果的正确性。由于网络延迟一般只有毫秒级,所以即使是事件时间语义,同样可以完成低延迟实时流处理的任务。
另外,除了事件时间和处理时间,Flink 还有一个摄入时间(Ingestion Time)的概念,它是指数据进入 Flink 数据流的时间,也就是 Source 算子读入数据的时间。摄入时间相当于是事件时间和处理时间的一个中和,它是把 Source 任务的处理时间,当作了数据的产生时间添加到数据里。这样一来,水位线(watermark)就基于这个时间直接生成,不需要单独指定了。这种时间语义可以保证比较好的正确性,同时又不会引入太大的延迟。它的具体行为跟事件时间非常像,可以当作特殊的事件时间来处理。
在 Flink 中,由于处理时间比较简单,早期版本默认的时间语义是处理时间;而考虑到事件时间在实际应用中更为广泛,从 1.12 版本开始,Flink 已经将事件时间作为了默认的时间语义
2. 水位线(Watermark)
2.1 事件时间和窗口
事件时间语义下,基于数据的时间戳,自定义了一个逻辑时钟。这个时钟的时间不会自动流逝;它的时间进展,就是靠着新到数据的时间戳来推动的。计算的过程可以完全不依赖处理时间(系统时间),不论什么时候进行统计处理,得到的结果都是正确的。

如图:事件时间语义类似于物流发车,到达车上的商品,生产时间是8 点 05 分,那么当前车上的时间就是 8 点 05 分;又来了一个 8 点 10 分生产的商品,现在车上的时间就是 8 点 10 分。直接用数据的时间戳来指示当前的时间进展,窗口的关闭自然以数据的时间戳等于窗口结束时间为准,这就不受网络传输延迟的影响了。对于8 点 59 分 59 秒生产出来的商品,到车上的时候不管实际时间(系统时间)是几点,当前时间就是 8 点 59 分 59 秒,所以它总是能赶上车的;而 9 点这班车,要等到 9 点整生产的商品到来,才认为时间到了 9 点,这时才正式发车。
2.2 水位线
在数据流中加入一个时钟标记,记录当前的事件时间;这个标记可以直接广播到下游,当下游任务收到这个标记,就可以更新自己的时钟了。由于类似于水流中用来做标志的记号,在 Flink 中,这种用来衡量事
件时间(Event Time)进展的标记,就被称作水位线(Watermark)
2.2.1 有序流中的水位线
在理想状态下,数据应该按照它们生成的先后顺序、排好队进入流中;也就是说,它们处理的过程会保持原先的顺序不变,遵守先来后到的原则。从每个数据中提取时间戳,就可以保证总是从小到大增长的,从而插入的水位线也会不断增长、事件时钟不断向前推进。
实际应用中,如果当前数据量非常大,可能会有很多数据的时间戳是相同的,这时每来一条数据就提取时间戳、插入水位线就做了大量的无用功。而且即使时间戳不同,同时涌来的数据时间差会非常小(比如几毫秒),往往对处理计算也没什么影响。所以为了提高效率,一般会每隔一段时间生成一个水位线,这个水位线的时间戳,就是当前最新数据的时间戳

注意:对于水位线的周期性生成,周期时间是指处理时间(系统时间),而不是事件时间
2.2.2 乱序流中的水位线
乱序指数据的先后顺序不一致,主要是基于数据的产生时间而言的

乱序流中的水位线,插入新的水位线时,要先判断一下时间戳是否比之前的大,否则就不再生成新的水位线

如果考虑到大量数据同时到来的处理效率,同样可以周期性地生成水位线。这时只需要保存一下之前所有数据中的最大时间戳,需要插入水位线时,就直接以它作为时间戳生成新的水位线

对于迟到数据,为了让窗口能够正确收集到迟到的数据,可以等上若干秒;即用当前已有数据的最大时间戳减去 若干秒,就是要插入的水位线的时间戳,如图水位线设置延迟2秒

注:
一个窗口所收集的数据,并不是之前所有已经到达的数据。因为数据属于哪个窗口,是由数据本身的时间戳决定的,一个窗口只会收集真正属于它的那些数据。如图尽管水位线 W(20)之前有时间戳为 22 的数据到来,10~20 秒的窗口中也不会收集这个数据,进行计算依然可以得到正确的结果

2.3 水位线特点
(1)水位线是插入到数据流中的一个标志
(2)水位线主要内容是一个时间戳,用来表示当前事件时间的进展
(3)水位线是基于数据的时间戳产生的
(4)水位线的时间戳必须是单调递增的,来保障,以确保任务的事件时间时钟向前推进
(5)水位线可以设置延迟,来保证正确处理乱序数据
(6)一个水位线 Watermark(t),表示在当前流中事件时间已经达到了时间戳 t, 这代表 t 之前的所有数据都到齐了,之后流中不会出现时间戳 t’ ≤ t 的数据
3. 如何生成水位线
3.1 生成水位线的总体原则
由于网络传输的延迟不确定,为了获取所有迟到数据,只能设置延迟时间。但等待的时间越长,处理的实时性越低。因此可以单独创建一个 Flink 作业来监控事件流,建立概率分布或者机器学习模型,学习事件的迟到规律。得到分布规律之后,就可以选择置信区间来确定延迟,作为水位线的生成策略了。如,数据的迟到时间服从μ=1,σ=1 的正态分布,那么设置水位线延迟为 3 秒,就可以保证至少 97.7%的数据可以

本文详细介绍了Flink中的事件时间和处理时间语义,重点解析了水位线的概念,包括其在有序流和乱序流中的应用,以及如何生成水位线。水位线作为事件时间推进的标志,确保了窗口操作的正确性,同时讨论了自定义水位线策略和内置策略。
最低0.47元/天 解锁文章
517

被折叠的 条评论
为什么被折叠?



