事件时间/处理时间/摄入时间java
Flink支持流程序中的不一样的时间通知。算法
处理时间:处理时间是指执行操做的机器系统时间。异步
当流程序运行在处理时间上时,全部的和时间相关的操做(好比时间窗口)都会使用运行各个操做运算的机器系统时钟。举个例子,一个小时单位的处理时间窗口会包含到达指定操做的全部记录在系统时钟是整个小时数之间。分布式
处理时间时最简单的时间通知,它须要流和机器之间的协做。它提供了最好的算法表现和最低的延迟。然而在分布式和异步环境中处理时间不提供可检测机制,由于这样会影响记录到达系统的速度(好比从消息队列中过来的),还有在系统中的算子之间的记录流动速度。ide
事件时间:事件时间是每一个发生在生产装置中的独立事件的时间。这个时间是在它们进入Flink和从记录中抽取的事件时间戳以前就包含在记录中的。一个小时单位的事件时间窗口包含拿到落地到那个小时内的事件时间戳,无论记录什么时候到达,无论他们的顺序如何。函数
尽管对于无序的事件,事件时间都会给出正确的结果,晚来的事件,或者葱备份或者持久化的日志中回放记录。在事件时间中,依赖数据的处理时间,不是在任何现实时钟时间。事件时间程序必须肯定怎样产生事件时间水印。而这个就是事件时间的信号流程的机制。下面来描述这个机制:google
事件时间流程老是发生在固定的延迟内,由于它自带的等待迟到的事件和无序的事件一段时间的属性。由于这个,事件时间程序常常和处理时间算子合并在一块儿用。idea
摄入时间:摄入时间是进入Flink的事件的时间。在源算子中每一个记录都能将源的当前时间做为一个时间戳,依赖于时间的操做就是这个时间戳。日志
摄入时间是位于事件时间和处理时间之间的概念。相比于处理时间,它的开销更小,可是给予了更多预测结果。由于摄入时间使用了平衡时间戳(在源就分配了),对于记录的不一样的窗口操做都会产生相同的时间戳,然而在处理时间上,每一个窗口操做都会将记录分配给不一样的窗口(基于本地系统时钟和任意传输时延)。code
相比较于事件时间,摄入时间程序不一样处理无序和迟到的事件,可是这个程序不必定义怎样去生成水印。
对于内部来讲,摄入时间更像是事件时间,可是有自动的时间戳分配和自动的水印生成。
设置时间特征
Flink DataStream程序的第一步通常都是设置一个基本的时间特征。这个设置定义了数据流源是怎么样执行的(好比说,他们分发了时间戳),还有窗口操做的时间通知的用法,像这样:KeyedStream.timeWindow(Time.seconds(30))。
下面的例子就展现了一个Flink程序是怎样在一个时间单位的窗口中聚合事件的。窗口的行为适配了时间特征函数。
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.setStreamTimeCharacteristic(TimeCharacteristic.ProcessingTime); // alternatively: // env.setStreamTimeCharacteristic(TimeCharacteristic.IngestionTime); // env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime); DataStream<MyEvent> stream = env.addSource(new FlinkKafkaConsumer09<MyEvent>(topic, schema, props)); stream .keyBy( (event) -> event.getUser() ) .timeWindow(Time.hours(1)) .reduce( (a, b) -> a.add(b) ) .addSink(...); val env = StreamExecutionEnvironment.getExecutionEnvironment env.setStreamTimeCharacteristic(TimeCharacteristic.ProcessingTime) // alternatively: // env.setStreamTimeCharacteristic(TimeCharacteristic.IngestionTime) // env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime) val stream: DataStream[MyEvent] = env.addSource(new FlinkKafkaConsumer09[MyEvent](topic, schema, props)) stream .keyBy( _.getUser ) .timeWindow(Time.hours(1)) .reduce( (a, b) => a.add(b) ) .addSink(...)
为了在事件时间上运行这个例子,这个程序须要使用为数据直接定义事件时间和水印的注入,或者程序必须在源以后注入一个时间戳发放者和水印生成器。这些函数描述了怎样进入事件时间戳,还有乱序的事件流是怎样体现的。
下面的一段描述了在时间戳和水印背后的通用机制。做为指导怎样在Flink DataStream API中使用时间戳分发器和水印生成器。
事件时间和水印
Flink从数据流模型中实现了许多技术。为了作一个关于事件时间和水印的很好的介绍,咱们看下下面的文章:
一个支持事件时间的流处理须要一种处理事件时间流程的措施。好比说,一个窗口操做间里了时间的窗口须要经过当事件时间在一个小时的末了经过的时候,这样这个操做就能够在流程中关闭窗口。
事件时间能够独立于处理时间进行。举个例子,在一个程序中一个操做的当前事件时间牢牢落后于处理时间,这两个时间几乎是同样的速度。从另外一个方面讲,另外的流程序会花费仅仅几秒的处理时间来处理持续数周的事件时间。经过已经存储在一个kafka(或者其余的消息队列中间件) topic中的缓冲区域快进一些历史数据。
Flink处理事件时间的流程的机制就是水印。水印做为数据流的一部分,获取一个时间戳t。这个水印代表了事件时间在这个流中有一个到达时间t。代表在流中没有一个带时间戳的元素,他的时间戳t‘是小于t的。
下面的图展现了带有逻辑时间戳的事件流,水印流转其中。在这个例子中事件是排好序的,代表水印仅仅是在流中周期性的标记。
水印对于无序的流来讲相当重要,就像下面的图中展现的同样,事件不是按照时间戳排好序的。通常一个水印是经过流中的点来表示的,全部的在一个指定时间戳的事件都会到达。当一个水印到达一个操做时,那个操做就能够将它的内部事件时间校准到和水印的值同样。
并行流中的水印
水印通常都是直接位于源函数以后的。每一个源函数的并行子任务都会产生独立的水印。这些韩素音定义了在特定并行源上的事件时间。
因为水印是流转在流程序中的,他们先于到达操做的事件时间。当一个操做先于事件时间时,他产生一个用于他的替代操做子的新水印下游。
一些算子来自多个输入流:一个联结,举个例子,做为keyBy()或者partition()函数的参数的算子的事件事件时这个输入流中最小的。输入流和操做一块儿更新他们的事件时间。
下面的图展现了一个在并行流中的事件和水印的例子,很明显能看到跟随事件时间的算子。