Flink事件时间、水印以及迟到数据处理的个人理解

本文详细介绍了Flink中处理事件时间的概念,包括处理时间、事件时间和摄取时间的区别,以及为什么需要窗口计算。重点讨论了水印的作用,如允许数据在一定范围内乱序,并解释了如何生成水印。此外,还探讨了迟到数据的处理,如窗口允许延迟和侧输出迟到数据的策略,以确保系统能正确处理实时流数据中的延迟和乱序问题。
摘要由CSDN通过智能技术生成

Flink中的时间概念

Flink在流式传输程序中支持不同的时间概念:

  • ProcessingTime: 处理时间,正在执行操作的机器的时间
  • EventTime: 事件时间,事件发生的时间
  • IngestionTime:摄取时间,时间进入Flink的时间
    在这里插入图片描述

区别

处理时间是一种比较简单的时间概念,不需要流和系统之间进行协调,可以提供最佳的性能和最低的延迟。但是在分布式环境中,多台机器的处理时间无法做到严格一致,无法提供确定性的保障。而事件时间是事件产生的时间,是Flink DataStream中的数据元素自身带有的、在其实际发生时记录的时间戳,具有业务含义,并与系统时间独立。在进入到 Flink 系统的时候,已经在 record 中进行记录,可以通过用提取事件时间戳的方式,保证在处理过程中,反映事件发生的先后关系。

设置时间语义

Flink DataStream程序的第一部分通常设置基准时间特征。该设置定义了数据流源的行为方式(例如,是否分配时间戳)

val env = StreamExecutionEnvironment.getExecutionEnvironment
// 事件时间
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
// 处理时间
env.setStreamTimeCharacteristic(TimeCharacteristic.ProcessingTime)
// 摄取时间
env.setStreamTimeCharacteristic(TimeCharacteristic.IngestionTime)

为什么需要窗口计算

我们知道流式数据集是没有边界的,数据会源源不断的发送到我们的系统中。流式计算最终的目的是去统计数据产生汇总结果的,而在无界数据集上,如果做一个全局的窗口统计,是不现实的。只有去划定一定大小的窗口范围去做计算,才能最终汇总到下游的系统中,用来分析和展示。

在 Flink 进行窗口计算的时候,需要去知道两个核心的信息:

  • 每个 Element 的 EventTime 时间戳?(在数据记录中指定即可)
  • 接入的数据,何时可以触发统计计算 ?(窗口的数据全部被接收完)

有序事件

假设在完美的条件下,数据都是严格有序,那么此时,流式计算引擎是可以正确计算出每个窗口的数据的。

在这里插入图片描述

无序事件

但是现实中,数据可能会因为各种各样的原因(系统延迟,网络延迟等)不是严格有序到达系统,甚至有的数据还会迟到很久,此时 Flink 需要有一种机制,允许数据可以在一定范围内乱序。这种机制就是水印。
在这里插入图片描述

有一个参数:MaxOutOfOrderness = 4,为最大乱序时间,意思是可以允许数据在多少范围内乱序,可以是 4 分钟,4 个小时 等。
水印的生成策略是,当前窗口最大事件时间戳减去 MaxOutOfOrderness 的值。
如上图,事件 7 会产生一个 w(3) 的水印,事件 11 会产生要给 w(7) 的水印,但是事件 9 ,是小于事件 11 的,此时不会触发水印的更新。事件 15 会产生一个 w(11) 的水印。
也就是说,水印反映了事件的整体流转的趋势,只会上升,不会下降。水印表示了所有小于水印值的事件都已经到达了窗口。
每当有新的最大时间戳出现时,就会产生新的 watermark

迟到事件

对于事件时间小于水印时间的事件,称为迟到事件。迟到事件是不会被纳入窗口统计的。
如下图,21 的事件进入系统之后,会产生 w(17) 的水印。而后来的 16 事件,由于小于当前水印时间 w(17),是不会被统计的了。

在这里插入图片描述
很显然,由于外部系统产生的数据往往不能及时、按序到达Flink系统,所以事件时间比处理时间有更强的不可预测性。为了能够准确地表达事件时间的处理进度,就必须用到水印。

Watermark

Flink水印的本质是DataStream中的一种特殊元素,每个水印都携带有一个时间戳。当时间戳为T的水印出现时,表示事件时间t <= T的数据都已经到达,即水印后面应该只能流入事件时间t > T的数据。也就是说,水印是Flink判断迟到数据的标准,同时也是窗口触发的标记。

watermark的特点:

  • 是一条通过自定义函数生成的特殊数据记录,直接插入到数据流中的。
  • 必须单调递增,因为watermark是用来表示系统语义上的时间,既然是时间,那么就必须递增
    对于乱序数据,怎么做到单调递增?
    很简单,就是一直把已有的watermark与新生成watermark的取最大值返回。
  • watermark与数据时间相关,是通过数据流中的数据时间产生的
    watermark是一个插入到数据流中的记录(自己写函数生成),生成的watermark通常是对时间戳的包装(即系统语义时间)。

watermark作用:

  • 提示根据watermark判断窗口是否触发关闭。

比如窗口时间间隔是5,那么会存在0-5、6-10、11-15等窗口。
0-5的窗口会把数据中时间在0-5之间的数据放入该窗口。但窗口的关闭是通过watermark的时间来判断,当watermark的时间大于5时,那么0-5这个窗口就会关闭,触发窗口聚合函数等操作。如果watermark一直小于5,0-5的窗口就一直不会关闭,那么在窗口未关闭的时候,数据流中数据的时间如果在0-5之间,那么这些数据就会放入0-5的窗口中,直到watermark大于5触发0-5的窗口关闭聚合操作为止。

所以,如果我们在定义生成watermark的函数中,如果把每次生成的watermark是由当前记录的时间减去n得到的,那么就会使得每个窗口多等待n的时间,如果在这多等待的n时间内,还有属于0-5时间的数据过来,那么还会放入0-5这个窗口中。(如果来的数据不是0~5的,那自然是去到他该进入的窗口中)

事件时间是如何从数据中提取的,水印又是如何产生的呢?

提取事件时间、产生水印

Flink提供了统一的DataStream.assignTimestampsAndWatermarks()方法来提取事件时间并同时产生水印,毕竟它们在处理过程中是紧密联系的。

val socketStream = env.socketTextStream
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值