时间语义:
时间本身就有着“流”的特性,它可以用来判断事件 发生的先后以及间隔;所以如果我们想要划定窗口来收集数据,一般就需要基于时间。对于批 处理来说, 这似乎没什么讨论的必要,因为数据都收集好了, 想怎么划分窗口都可以; 而对于 流处理来说,如果想处理更加实时, 就必须对时间有更加精细的控制。
Flink 中的时间语义:Flink 是一个分布式处理 系统。分布式架构最大的特点, 就是节点彼此独立、互不影响,这带来了更高的吞吐量和容错 性;但有利必有弊,在分布式系统中,节点“各自为政”,没有统一的时钟, 数据和控制信息都通过网络进 行传输。比如现在有一个任务是窗口聚合,我们希望将每个小时的数据收集起来进行统计处理。 而对于并行的窗口子任务,它们所在节点不同,系统时间也会有差异; 比如我们希望统计8点 ~ 9点的数据时,对并行任务来说其实并不是“同时”的, 收集到的数据也会有误差。
既然一个集群中有 JobManager 作为管理者, 是不是让它统一向所有 TaskManager 发送 同步时钟信号就行了呢? 这也是不行的。因为网络传输会有延迟, 而且这延迟是不确定的, 所 以 JobManager 发出的同步信号无法同时到达所有节点;想要拥有一个全局统一的时钟, 在分 布式系统里是做不到的。在流式处理的过程中, 数据是在不同的节点间不停流动的, 同样 也会有网络传输的延迟。这样一来, 当上下游任务需要跨节点传输数据时, 它们对于“时间” 的理解也会有所不同。例如, 上游任务在 8 点 59 分 59 秒发出一条数据, 到下游要做窗口计算 时已经是 9 点零 1 秒了, 那这条数据到底该不该被收到 8 点~9 点的窗口呢?
所以, 当我们希望对数据按照时间窗口来进行收集计算时,“时间”到底以谁为标准就非 常重要了。
- 处理时间(Processing Time)
就是指执行处理操作机器的系统时间。 只看窗口任务处理这条数据时当前的系统时间。比如数据 8 点 59 分 59 秒产生,而窗口计算时 时间是 9 点零 1 秒,那么这条数据就属于 9点~10 点的窗口;如果数据传输非常快,9 点之前就 到了窗口任务,那么它就属于 8 点~9 点的窗口了。 这种方法非常简单粗暴,不需要各个节点之间进行协调同步,也不需要考虑数据在流中的 位置。所以处理时间是最简单的时间语义。
- 事件时间(Event Time)
事件时间,是指每个事件在对应的设备上发生的时间,也就是数据生成的时间。 数据一旦产生,这个时间自然就确定了。其实就是这条数据记录的时间戳(Timestamp)。 在事件时间语义下,我们对于时间的衡量,就不看任何机器的系统时间,而是依赖于数 据本身。 这相当于任务处理的时候本身是没有时钟的,所以只好来一个数据就 问一下“现在几点了”;而数据本身也没有表,只有一个自带的“出厂时间”,于是任务就基于这 个时间来确定自己的时钟。由于流处理中数据是源源不断产生的,一般来说,先产生的数据也 会先被处理,所以当任务不停地接到数据时,它们的时间戳也基本上是不断增长的,就可以代 表时间的推进。 当然我们会发现,这里有个前提,就是“先产生的数据先被处理”,这要求我们可以保证 数据到达的顺序。但是由于分布式系统中网络传输延迟的不确定性,实际应用中我们要面对的 数据流往往是乱序的。在这种情况下,就不能简单地把数据自带的时间戳当作时钟了,而需要 用另外的标志来表示事件时间进展,在 Flink 中把它叫作事件时间的“水位线”(Watermarks)。
哪种时间语义更重要:
水位线:
-
事件时间和窗口: